<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
     
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      
<section class="cover">
    
      
      <a class="forkMe" href="https://github.com/Shen-Yu/hexo-theme-ayer"
        target="_blank"><img width="149" height="149" src="/images/forkme.png"
          class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
    
  <div class="cover-frame">
    <div class="bg-box">
      <img src="/images/cover1.jpg" alt="image frame" />
    </div>
    <div class="cover-inner text-center text-white">
      <h1><a href="/">Hexo</a></h1>
      <div id="subtitle-box">
        
        <span id="subtitle"></span>
        
      </div>
      <div>
        
      </div>
    </div>
  </div>
  <div class="cover-learn-more">
    <a href="javascript:void(0)" class="anchor"><i class="ri-arrow-down-line"></i></a>
  </div>
</section>



<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js"></script>


<!-- Subtitle -->

  <script>
    try {
      var typed = new Typed("#subtitle", {
        strings: ['面朝大海，春暖花开', '何来天才，唯有苦练', '集中一点，登峰造极'],
        startDelay: 0,
        typeSpeed: 200,
        loop: true,
        backSpeed: 100,
        showCursor: true
      });
    } catch (err) {
      console.log(err)
    }
  </script>
  
<div id="main">
  <section class="outer">
  
  

<div class="notice" style="margin-top:50px">
    <i class="ri-heart-fill"></i>
    <div class="notice-content" id="broad"></div>
</div>
<script type="text/javascript">
    fetch('https://v1.hitokoto.cn')
        .then(response => response.json())
        .then(data => {
            document.getElementById("broad").innerHTML = data.hitokoto;
        })
        .catch(console.error)
</script>

<style>
    .notice {
        padding: 20px;
        border: 1px dashed #e6e6e6;
        color: #969696;
        position: relative;
        display: inline-block;
        width: 100%;
        background: #fbfbfb50;
        border-radius: 10px;
    }

    .notice i {
        float: left;
        color: #999;
        font-size: 16px;
        padding-right: 10px;
        vertical-align: middle;
        margin-top: -2px;
    }

    .notice-content {
        display: initial;
        vertical-align: middle;
    }
</style>
  
  <article class="articles">
    
    
    
    
    <article
  id="post-k8s/一文讲清楚Ceph分布式存储"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/%E4%B8%80%E6%96%87%E8%AE%B2%E6%B8%85%E6%A5%9ACeph%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/"
    >一文讲清楚Ceph分布式存储.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/%E4%B8%80%E6%96%87%E8%AE%B2%E6%B8%85%E6%A5%9ACeph%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="一文讲清楚Ceph分布式存储"><a href="#一文讲清楚Ceph分布式存储" class="headerlink" title="一文讲清楚Ceph分布式存储"></a>一文讲清楚Ceph分布式存储</h1><p>Ceph项目最早起源于Sage就读博士期间的工作（最早的成果于2004年发表），并随后贡献给开源社区。在经过了数年的发展之后，目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合以支持虚拟机镜像的后端存储。但是在2014年OpenStack火爆的时候、Ceph并不被很多人所接受。当时Ceph并不稳定（Ceph发布的第四个版本 Dumpling v0.67），而且架构新颖，复杂，当时人们对Ceph在生产落地如何保障数据的安全，数据的一致性存在怀疑。</p>
<p>随着OpenStack的快速发展，给Ceph的发展注入了强心剂，越来越多的人使用Ceph作为OpenStack的底层共享存储，Ceph在中国的社区也蓬勃发展起来。近两年OpenStack火爆度不及当年，借助于云原生尤其是Kubernetes技术的发展，作为底层存储的基石，Ceph再次发力，为Kubernets有状态化业务提供了存储机制的实现。</p>
<h2 id="存储发展史"><a href="#存储发展史" class="headerlink" title="存储发展史"></a>存储发展史</h2><p>企业中使用存储按照其功能，使用场景，一直在持续发展和迭代，大体上可以分为四个阶段：</p>
<p>•<strong>DAS：</strong>Direct Attached Storage，即直连存储，第一代存储系统，通过SCSI总线扩展至一个外部的存储，磁带整列，作为服务器扩展的一部分；•<strong>NAS：</strong>Network Attached Storage，即网络附加存储，通过网络协议如NFS远程获取后端文件服务器共享的存储空间，将文件存储单独分离出来；•<strong>SAN：</strong>Storage Area Network，即存储区域网络，分为IP-SAN和FC-SAN，即通过TCP/IP协议和FC(Fiber Channel)光纤协议连接到存储服务器；•<strong>Object Storage：</strong>即对象存储，随着大数据的发展，越来越多的图片，视频，音频静态文件存储需求，动仄PB以上的存储空间，需无限扩展。</p>
<p>存储的发展，根据不同的阶段诞生了不同的存储解决方案，每一种存储都有它当时的历史诞生的环境以及应用场景，解决的问题和优缺点。</p>
<p><strong>区别如下：</strong></p>
<p>DAS 直连存储服务器使用 SCSI 或 FC 协议连接到存储阵列、通过 SCSI 总线和 FC 光纤协议类型进行数据传输；例如一块有空间大小的裸磁盘：/dev/sdb。DAS存储虽然组网简单、成本低廉但是可扩展性有限、无法多主机实现共享、目前已经很少使用了。</p>
<p>NAS网络存储服务器使用TCP网络协议连接至文件共享存储、常见的有NFS、CIFS协议等；通过网络的方式映射存储中的一个目录到目标主机，如/data。NAS网络存储使用简单，通过IP协议实现互相访问，多台主机可以同时共享同一个存储。但是NAS网络存储的性能有限，可靠性不是很高。</p>
<p>SAN存储区域网络服务器使用一个存储区域网络IP或FC连接到存储阵列、常见的SAN协议类型有IP-SAN和FC-SAN。SAN存储区域网络的性能非常好、可扩展性强；但是成本特别高、尤其是FC存储网络：因为需要用到HBA卡、FC交换机和支持FC接口的存储。</p>
<p>Object Storage对象存储通过网络使用API访问一个无限扩展的分布式存储系统、兼容于S3风格、原生PUT/GET等协议类型。表现形式就是可以无限使用存储空间，通过PUT/GET无限上传和下载。可扩展性极强、使用简单，但是只使用于静态不可编辑文件，无法为服务器提供块级别存储。</p>
<p>综上、企业中不同场景使用的存储，使用表现形式无非是这三种：磁盘（块存储设备），挂载至目录像本地文件一样使用（文件共享存储），通过API向存储系统中上传PUT和下载GET文件（对象存储）。</p>
<h2 id="什么是Ceph？"><a href="#什么是Ceph？" class="headerlink" title="什么是Ceph？"></a>什么是Ceph？</h2><p>接下来我们要讲讲Ceph，那么到底什么是Ceph呢？Ceph能够提供企业中三种常见的存储需求：块存储、文件存储和对象存储，正如Ceph官方所定义的一样“Ceph uniquely delivers object, block, and file storage in one unified system.”，Ceph在一个统一的存储系统中同时提供了对象存储、块存储和文件存储，即Ceph是一个统一存储，能够将企业企业中的三种存储需求统一汇总到一个存储系统中，并提供分布式、横向扩展，高度可靠性的存储系统，Ceph存储提供的三大存储接口：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601174709035.png" alt="null">什么是Ceph分布式存储？</p>
<p>上图详见官方文档：<a target="_blank" rel="noopener" href="http://docs.ceph.org.cn/">http://docs.ceph.org.cn/</a></p>
<p><strong>1、CEPH OBJECT STORE对象存储，包含功能，特性如下：</strong></p>
<p>•RESTful Interface RESTful风格接口；•S3- and Swift-compliant APIs 提供兼容于S3和Swfit风格API；•S3-style subdomains S3风格的目录风格；•Unified S3/Swift namespace 统一扁平的S3/Swift命名空间，即所有的对象存放在同一个平面上；•User management 提供用户管理认证接入；•Usage tracking 使用情况追踪；•Striped objects 对象切割，将一个大文件切割为多个小文件（objects）；•Cloud solution integration 云计算解决方案即成，可以与Swfit对象存储即成；•Multi-site deployment 多站点部署，保障可靠性；•Multi-site replication 多站点复制，提供容灾方案。</p>
<p><strong>2、CEPH BLOCK DEVICE块存储，包含功能，特性如下：</strong></p>
<p>•Thin-provisioned 瘦分配，即先分配特定存储大小，随着使用实际使用空间的增长而占用存储空间，避免空间占用；•Images up to 16 exabytes 耽搁景象最大能支持16EB；•Configurable striping 可配置的切片，默认是4M；•In-memory caching 内存缓存；•Snapshots 支持快照，将当时某个状态记录下载；•Copy-on-write cloning Copy-on-write克隆复制功能，即制作某个镜像实现快速克隆，子镜像依赖于母镜像；•Kernel driver support 内核驱动支持，即rbd内核模块；•KVM/libvirt support 支持KVM/libvirt，实现与云平台如openstack，cloudstack集成的基础；•Back-end for cloud solutions 云计算多后端解决方案，即为openstack，kubernetes提供后端存储；•Incremental backup 增量备份；•Disaster recovery (multisite asynchronous replication) 灾难恢复，通过多站点异步复制，实现数据镜像拷贝。</p>
<p><strong>3、CEPH FILE SYSTEM文件存储，包含功能，特性如下：</strong></p>
<p>•POSIX-compliant semantics POSIX风格接口；•Separates metadata from data 元数据metadata和数据data分开存储；•Dynamic rebalancing 动态数据均衡；•Subdirectory snapshots 子目录快照；•Configurable striping 可配置切割大小；•Kernel driver support 内核驱动支持，即CephFS；•FUSE support 支持FUSE风格；•NFS/CIFS deployable 支持NFS/CIFS形式部署；•Use with Hadoop (replace HDFS) 可支持与Hadoop继承，替换HDFS存储。</p>
<p>通俗点讲：Ceph提供了三种存储接口：块存储RBD，对象存储RGW和文件存储CephFS，每种存储都有其相应的功能和特性。</p>
<h2 id="Ceph存储架构"><a href="#Ceph存储架构" class="headerlink" title="Ceph存储架构"></a>Ceph存储架构</h2><p>Ceph 独一无二地用统一的系统提供了对象、块、和文件存储功能，它可靠性高、管理简便、并且是自由软件。Ceph 的强大足以改变贵公司的 IT 基础架构、和管理海量数据的能力。Ceph 可提供极大的伸缩性——供成千用户访问 PB 乃至 EB 级的数据。Ceph 节点以普通硬件和智能守护进程作为支撑点， Ceph 存储集群组织起了大量节点，它们之间靠相互通讯来复制数据、并动态地重分布数据。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601174708958.png" alt="null">什么是Ceph分布式存储？</p>
<p>接下来，我们先来看一下Ceph的存储架构，了解Ceph的分布式架构，功能组件和涉及相关概念。Ceph分布式集群是建立在RADOS算法之上的，RADOS是一个可扩展性，高可靠的存储服务算法，是Ceph的实现的基础。Ceph有两个重要的组件组成：<strong>Ceph Monitors（Ceph监视器）和Ceph OSDs（Ceph OSD 守护进程）</strong>。</p>
<p>其中Ceph Monitor作为集群中的控制中心，拥有整个集群的状态信息，各个组件如OSDs将自己的状态信息报告给Ceph Monitor这个总司令，由此可以可知，Ceph Monitor这个总司令肩负起整个集群协调工作；同时Ceph Monitor还负责将集群的指挥工作，将集群的状态同步给客户端，客户端根据Ceph Monitor发送的集群状态信息可以获取到集群的状态，当集群状态有变化如OSD增加或故障时，Ceph Monitor会负责更新集群状态并下发给客户端。Ceph Monitor的重要不言而喻，为了保障集群的可用性，需要部署高可用，一般需要部署2n+1个节点，如3个或5个Ceph Monitor节点。</p>
<p>什么是集群的状态呢？Ceph Monitor中保存的集群状态根据其功能角色的不同，分为以下几个map状态表：</p>
<p>•Monitor Maps，集群Ceph Monitor集群的节点状态，通过ceph mon dump可以获取；•OSD Maps，集群数据存储节点的状态表，记录集群中OSD状态变化，通过ceph osd dump可以获取；•PGs Maps，PGs即placement group，表示在OSD中的分布式方式，通过ceph pg dump可以获取；•Crush Maps，Crush包含资源池pool在存储中的映射路径方式，即数据是如何分布的；•MDS Maps，CephFS依赖的MDS管理组件，可通过ceph mds dump获取，用于追踪MDS状态。</p>
<p>除了Ceph Monitor之外，还有一个重要的组件是OSD，集群中通常有多个OSD组成，OSD即Object Storage Daemon，负责Ceph集群中真正数据存储的功能，也就是我们的数据最终都会写入到OSD中。除了Monitor之外，根据Ceph提供的不同功能，还有其他组件，包括：</p>
<p>•Ceph Monitors（ceph-mon）；•Ceph OSDs（ceph-osd）；•Ceph MDS（ceph-mds），用于提供CephFS文件存储，提供文件存储所需元数据管理；•Ceph RGW（ceph-rgw），用于提供Ceph对象存储网关，提供存储网关接入；•Ceph Manager（ceph-mgr），提供集群状态监控和性能监控。</p>
<p>注：Ceph Monitor监视器维护着集群运行图的主副本。一个监视器集群确保了当某个监视器失效时的高可用性。存储集群客户端向 Ceph Monitor 监视器索取集群运行图的最新副本。而Ceph OSD 守护进程检查自身状态、以及其它 OSD 的状态，并报告给监视器们。存储集群的客户端和各个 Ceph OSD 守护进程使用 CRUSH 算法高效地计算数据位置，而不是依赖于一个中心化的查询表。它的高级功能包括：基于 librados的原生存储接口、和多种基于 librados 的服务接口。</p>
<h2 id="Ceph数据的存储"><a href="#Ceph数据的存储" class="headerlink" title="Ceph数据的存储"></a>Ceph数据的存储</h2><p>了解完Ceph的架构后，我们先来了解一下Ceph的读写流程，期间会涉及到CRUSH，PGs等这些概念，我们从一个最基本的的概念入手：Ceph中一切皆对象，不管是RBD块存储接口，RGW对象存储接口还是文件存储CephFS接口，其存储如到Ceph中的数据均可以看作是一个对象，一个文件需要切割为多个对象（object），然后将object存储到OSD中，如下图：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601174708894.png" alt="null">什么是Ceph分布式存储？</p>
<p>注：Ceph 存储集群从 <em>Ceph 客户端</em>接收数据——不管是来自 <em>Ceph 块设备</em>、 <em>Ceph 对象存储</em>、 <em>Ceph 文件系统</em>、还是基于 <code>librados</code> 的自定义实现——并存储为对象。每个对象是文件系统中的一个文件，它们存储在<em>对象存储设备</em>上。由 Ceph OSD 守护进程处理存储设备上的读/写操作。</p>
<p>那么，这些切割后的object怎么选择到对应的OSD存储节点呢，这需要依赖于Ceph的智能调度算法CRUSH，通过CRUSH算法将object调度到合适的OSD节点上，不管是客户端还是OSD，均使用CRUSH算法来计算object在集群中OSD的位置信息，同时保障object的副本能落到合适的OSD节点上，关于CRUSH算法的实现比较复杂，详情可以参考：CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data。</p>
<p>Ceph OSD 在扁平的命名空间内把所有数据存储为对象（也就是没有目录层次）。对象包含一个标识符、二进制数据、和由名字/值对组成的元数据，元数据语义完全取决于 <em>Ceph 客户端</em>。例如， CephFS 用元数据存储文件属性，如文件所有者、创建日期、最后修改日期等等。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601174709042.png" alt="null">什么是Ceph分布式存储？</p>
<p>object调度到OSD节点上，如果一个文件发生了变动或OSD出现了异常，以一个100G的文件为例，每个object默认为4M，将会切割为25600个object，如果Ceph集群需要正对每个object都进行调度的话，可想而知，在一个大规模集群中，crush的调度将会变得异常的繁重。因此，Ceph引入了另外一个概念PG，PG是Place Group即放置组，可以简单理解为一个装载object的容器，object将映射到PG中，PG最终会调度到某个具体的OSD上，因此CRUSH由object调度转为PG的调度，而PG的数量是相对固定的，因此集群分布时调度相对没有那么繁重，同时，当某个OSD异常时，CRUSH调度算法只需将其上的PG调度至其他OSD上（而不是将其上的object进行调度）。Ceph的整个数据调度写入流程如下图：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601174709048.webp" alt="null">什么是Ceph分布式存储？</p>
<p>•一个文件将会切割为多个object（如1G文件每个object为4M将切割为256个），每个object会由一个由innode(ino)和object编号(ono)组成oid，即object id，oid是真个集群唯一，唯一标识object对象；•针对object id做hash并做取模运算，从而获取到pgid，即place group id，通过hash+mask获取到PGs ID，PG是存储object的容器；•Ceph通过CRUSH算法，将pgid进行运算，找到当前集群最适合存储PG的OSD节点，如osd1和osd2（假设为2个副本）；•PG的数据最终写入到OSD节点，完成数据的写入过程，当然这里会涉及到多副本，一份数据写多副本。</p>
<p>好了、现在我们对Ceph有了基本认识了。后面我们讲继续了解Ceph伸缩性和高可用性；动态集群管理；纠删编码；缓存分级等内容。当然、我们也会具体了解到如纠删编码中关于读出和写入编码块，被中断的完全写等更细的内容。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/如何在Kubernetes中配置基于NFS的持久卷"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/%E5%A6%82%E4%BD%95%E5%9C%A8Kubernetes%E4%B8%AD%E9%85%8D%E7%BD%AE%E5%9F%BA%E4%BA%8ENFS%E7%9A%84%E6%8C%81%E4%B9%85%E5%8D%B7/"
    >如何在Kubernetes中配置基于NFS的持久卷.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/%E5%A6%82%E4%BD%95%E5%9C%A8Kubernetes%E4%B8%AD%E9%85%8D%E7%BD%AE%E5%9F%BA%E4%BA%8ENFS%E7%9A%84%E6%8C%81%E4%B9%85%E5%8D%B7/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="如何在Kubernetes中配置基于NFS的持久卷"><a href="#如何在Kubernetes中配置基于NFS的持久卷" class="headerlink" title="如何在Kubernetes中配置基于NFS的持久卷"></a>如何在Kubernetes中配置基于NFS的持久卷</h1><p>建议将Pod的数据放入某个持久卷中，以便即使Pod终止后也可以使用这些数据。在Kubernetes（k8s）中，可以在Pod中使用基于NFS的持久卷。在本文中，我们将学习如何配置持久卷和持久卷声明，然后我们将讨论如何通过k8s pod中的声明名称使用持久卷。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601019057636.png" alt="img"></p>
<p>我假设我们有一个功能正常的k8s集群和NFS Server。以下是实验室设置的详细信息:</p>
<ul>
<li>NFS服务器IP = 192.168.1.40</li>
<li>NFS共享目录 = /opt/k8s-pods/data</li>
<li>K8s群集 = 一个主节点和两个工作节点</li>
</ul>
<p><strong>注意：</strong> 确保可以从worker节点访问NFS服务器，并尝试在每个worker节点上安装nfs共享以进行测试。</p>
<p>在nfs共享中创建一个index.html文件，因为我们将在本文后面的nginx pod中挂载该共享。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ echo &quot;Hello, NFS Storage NGINX&quot; &gt; &#x2F;opt&#x2F;k8s-pods&#x2F;data&#x2F;index.html</span><br></pre></td></tr></table></figure>

<h3 id="配置基于NFS的PV（永久卷）"><a href="#配置基于NFS的PV（永久卷）" class="headerlink" title="配置基于NFS的PV（永久卷）"></a>配置基于NFS的PV（永久卷）</h3><p>要在K8s中创建基于NFS的持久卷，请在主节点上使用以下内容创建yaml文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ vim nfs-pv.yaml</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: PersistentVolume</span><br><span class="line">metadata:</span><br><span class="line">  name: nfs-pv</span><br><span class="line">spec:</span><br><span class="line">  capacity:</span><br><span class="line">    storage: 10Gi</span><br><span class="line">  volumeMode: Filesystem</span><br><span class="line">  accessModes:</span><br><span class="line">    - ReadWriteMany</span><br><span class="line">  persistentVolumeReclaimPolicy: Recycle</span><br><span class="line">  storageClassName: nfs</span><br><span class="line">  mountOptions:</span><br><span class="line">    - hard</span><br><span class="line">    - nfsvers&#x3D;4.1</span><br><span class="line">  nfs:</span><br><span class="line">    path: &#x2F;opt&#x2F;k8s-pods&#x2F;data</span><br><span class="line">    server: 192.168.1.40</span><br></pre></td></tr></table></figure>

<p>保存并退出文件</p>
<p><img src="http://iubest.gitee.io/pic/640-1601019056801.png" alt="img"></p>
<p>现在使用上面创建的yaml文件创建持久卷，运行：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ kubectl create -f nfs-pv.yaml</span><br><span class="line">persistentvolume&#x2F;nfs-pv created</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>运行以下kubectl命令以验证持久卷的状态：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ kubectl get pv</span><br><span class="line">NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE</span><br><span class="line">nfs-pv   10Gi       RWX            Recycle          Available           nfs                     20s</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>以上输出确认PV已成功创建并且可用。</p>
<h3 id="配置持久卷声明"><a href="#配置持久卷声明" class="headerlink" title="配置持久卷声明"></a>配置持久卷声明</h3><p>要在Pod内挂载持久卷，我们必须指定其持久卷声明。因此，让我们使用以下YAML文件创建持久卷声明:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ vi nfs-pvc.yaml</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: PersistentVolumeClaim</span><br><span class="line">metadata:</span><br><span class="line">  name: nfs-pvc</span><br><span class="line">spec:</span><br><span class="line">  storageClassName: nfs</span><br><span class="line">  accessModes:</span><br><span class="line">    - ReadWriteMany</span><br><span class="line">  resources:</span><br><span class="line">    requests:</span><br><span class="line">      storage: 10Gi</span><br></pre></td></tr></table></figure>

<p>保存并退出文件。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601019077983.png" alt="img"></p>
<p>运行下面的kubectl命令以使用上面的yaml文件创建pvc:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ kubectl create -f nfs-pvc.yaml</span><br><span class="line">persistentvolumeclaim&#x2F;nfs-pvc created</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>执行完上述操作后，控制平面将使用相同的存储类名称查找满足声明要求的持久卷，然后将声明绑定到持久卷，示例如下所示：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ kubectl get pvc nfs-pvc</span><br><span class="line">NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE</span><br><span class="line">nfs-pvc   Bound    nfs-pv   10Gi       RWX            nfs            3m54s</span><br><span class="line">[kadmin@k8s-master ~]$</span><br><span class="line">[kadmin@k8s-master ~]$ kubectl get pv nfs-pv</span><br><span class="line">NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE</span><br><span class="line">nfs-pv   10Gi       RWX            Recycle          Bound    default&#x2F;nfs-pvc   nfs                     18m</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>以上输出确认声明（nfs-pvc）与持久卷（nfs-pv）绑定。</p>
<p>现在我们准备在pod旁边使用基于nfs的持久卷。</p>
<h3 id="在Pod中使用基于NFS的持久卷"><a href="#在Pod中使用基于NFS的持久卷" class="headerlink" title="在Pod中使用基于NFS的持久卷"></a>在Pod中使用基于NFS的持久卷</h3><p>在yaml文件下面创建一个nginx pod，它将在<code>/usr/share/nginx/html</code>上挂载持久卷声明。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ vi nfs-pv-pod</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Pod</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-pv-pod</span><br><span class="line">spec:</span><br><span class="line">  volumes:</span><br><span class="line">    - name: nginx-pv-storage</span><br><span class="line">      persistentVolumeClaim:</span><br><span class="line">        claimName: nfs-pvc</span><br><span class="line">  containers:</span><br><span class="line">    - name: nginx</span><br><span class="line">      image: nginx</span><br><span class="line">      ports:</span><br><span class="line">        - containerPort: 80</span><br><span class="line">          name: &quot;nginx-server&quot;</span><br><span class="line">      volumeMounts:</span><br><span class="line">        - mountPath: &quot;&#x2F;usr&#x2F;share&#x2F;nginx&#x2F;html&quot;</span><br><span class="line">          name: nginx-pv-storage</span><br></pre></td></tr></table></figure>

<p>保存并关闭文件。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601019057637.png" alt="img"></p>
<p>现在使用上面的yaml文件创建pod，运行:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ kubectl create -f nfs-pv-pod.yaml</span><br><span class="line">pod&#x2F;nginx-pv-pod created</span><br><span class="line">[kadmin@k8s-master ~]$</span><br><span class="line">[kadmin@k8s-master ~]$ kubectl get pod nginx-pv-pod -o wide</span><br><span class="line">NAME           READY   STATUS    RESTARTS   AGE   IP              NODE           NOMINATED NODE   READINESS GATES</span><br><span class="line">nginx-pv-pod   1&#x2F;1     Running   0          66s   172.16.140.28   k8s-worker-2   &lt;none&gt;           &lt;none&gt;</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>注意：要获取有关pod的更多详细信息，可使用 <code>kubectl describe pod </code>命令。</p>
<p>以上命令输出确认已成功创建容器。现在尝试使用curl命令访问nginx页面：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[kadmin@k8s-master ~]$ curl http:&#x2F;&#x2F;172.16.140.28</span><br><span class="line">Hello, NFS Storage NGINX</span><br><span class="line">[kadmin@k8s-master ~]$</span><br></pre></td></tr></table></figure>

<p>完美，上面<code>curl</code>命令的输出确认了持久卷已正确挂载到pod中，因为我们可以获取NFS共享上<code>index.html</code>文件的内容。</p>
<blockquote>
<p><strong><em>作者：Pradeep Kumar  译者：Yue Yong<br>原文来自：<a target="_blank" rel="noopener" href="https://www.linuxtechi.com/configure-nfs-persistent-volume-kubernetes/">https://www.linuxtechi.com/configure-nfs-persistent-volume-kubernetes\</a></em></strong></p>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/浅谈 Kubernetes 网络模型（CNI）"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/%E6%B5%85%E8%B0%88%20Kubernetes%20%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B%EF%BC%88CNI%EF%BC%89/"
    >浅谈 Kubernetes 网络模型（CNI）.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/%E6%B5%85%E8%B0%88%20Kubernetes%20%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B%EF%BC%88CNI%EF%BC%89/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="浅谈-Kubernetes-网络模型（CNI）"><a href="#浅谈-Kubernetes-网络模型（CNI）" class="headerlink" title="浅谈 Kubernetes 网络模型（CNI）"></a>浅谈 Kubernetes 网络模型（CNI）</h1><p>Kubernetes通过整合规模庞大的容器实例形成集群，这些容器实例可能运行在异构的底层网络环境中，如何保证这些容器间的互通是实际生产环境中首要考虑的问题之一。</p>
<h2 id="Kubernetes网络基本要求"><a href="#Kubernetes网络基本要求" class="headerlink" title="Kubernetes网络基本要求"></a>Kubernetes网络基本要求</h2><p>Kubernetes对容器技术做了更多的抽象，其中最重要的一点是提出pod的概念，pod是Kubernetes资源调度的基本单元，我们可以简单地认为pod是容器的一种延伸扩展，从网络的角度来看，pod必须满足以下条件：</p>
<ol>
<li>每一个Pod都有一个独特的IP地址，所有pod都在一个可以直接连通的、扁平的网络空间中</li>
<li>同一个pod内的所有容器共享同一个netns网络命名空间</li>
</ol>
<p><img src="http://iubest.gitee.io/pic/640-1601003388781.png" alt="img"></p>
<p>基于这样的基本要求，我们可以知道：</p>
<ol>
<li>同一个pod内的所有容器之间共享端口，可直接通过<code>localhost</code>+<code>端口</code>来访问</li>
<li>由于每个pod有单独的IP，所以不需要考虑容器端口与主机端口映射以及端口冲突问题</li>
</ol>
<p>事实上，Kubernetes进一步确定了对一个合格集群网络的基本要求：</p>
<ol>
<li>任意两个pod之间其实是可以直接通信的，无需显式地使用NAT进行地址的转换；</li>
<li>任意集群节点node与任意pod之间是可以直接通信的，无需使用明显的地址转换，反之亦然；</li>
<li>任意pod看到自己的IP跟别人看见它所用的IP是一样的，中间不能经过地址转换；</li>
</ol>
<p>也就是说，必须同时满足以上三点的网络模型才能适用于kubernetes，事实上，在早期的Kubernetes中，并没有什么网络标准，只是提出了以上基本要求，只有满足这些要求的网络才可以部署Kubernetes，基于这样的底层网络假设，Kubernetes设计了<code>pod-deployment-service</code>的经典三层服务访问机制。直到1.1发布，Kubernetes才开始采用全新的CNI(Container Network Interface)网络标准。</p>
<h2 id="CNI"><a href="#CNI" class="headerlink" title="CNI"></a>CNI</h2><p>其实，我们在前面介绍容器网络的时候，就提到了CNI网络规范，CNI相对于CNM(Container Network Model)对开发者的约束更少，更开放，不依赖于Docker。事实上，CNI规范确实非常简单，详见：<a target="_blank" rel="noopener" href="https://github.com/containernetworking/cni/blob/master/SPEC.md">https://github.com/containernetworking/cni/blob/master/SPEC.md</a></p>
<p><img src="http://iubest.gitee.io/pic/640-1601003388889.png" alt="img"></p>
<p>实现一个CNI网络插件只需要一个配置文件和一个可执行的文件：</p>
<ul>
<li>配置文件描述插件的版本、名称、描述等基本信息</li>
<li>可执行文件会被上层的容器管理平台调用，一个CNI可执行文件自需要实现将容器加入到网络的ADD操作以及将容器从网络中删除的DEL操作（以及一个可选的VERSION查看版本操作）</li>
</ul>
<p>Kubernetes使用CNI网络插件的基本工作流程：</p>
<ol>
<li>kubelet先创建<code>pause</code>容器生成对应的netns网络命名空间</li>
<li>根据配置调用具体的CNI插件，可以配置成CNI插件链来进行链式调用</li>
<li>当CNI插件被调用时，它根据环境变量以及命令行参数来获得网络命名空间netns、容器的网络设备等必要信息，然后执行ADD操作</li>
<li>CNI插件给pause容器配置正确的网络，pod中其他的容器都是用pause容器的网络</li>
</ol>
<p>如果不清楚什么是<code>pause</code>容器，它在pod中处于什么样的位置，请查看之前的笔记：<a target="_blank" rel="noopener" href="https://morven.life/notes/from-container-to-pod/">https://morven.life/notes/from-container-to-pod/</a></p>
<h2 id="pod网络模型"><a href="#pod网络模型" class="headerlink" title="pod网络模型"></a>pod网络模型</h2><p>要了解kubernetes网络模型的实现原理，我们就要从单个pod入手，事实上，一旦熟悉了单个pod的网络模型，就会发现kubernetes网络模型基本遵循和容器网络模型一样的原理。</p>
<p>通过前面的笔记从docker容器到pod，我们知道pod启动的时候先创建<code>pause</code>容器生成对应的netns网络命名空间，然后其他容器共享<code>pause</code>容器创建的网络命名空间。而对于单个容器的网络模型我们之前也介绍过，主要就是通过<code>docker0</code>网桥设备与veth设备对连接不同的容器网络命名空间，由此，我们可以得到如下图所示的单个pod网络模型的创建过程：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003389014.png" alt="img"></p>
<p>可以看到，同一个pod里面的其他容器共享<code>pause</code>容器创建的网络命名空间，也就是说，所有的容器共享相同的网络设备，路由表设置，服务端口等信息，仿佛是在同一台机器上运行的不同进程，所以这些容器之间可以直接通过<code>localhost</code>与对应的端口通信；对于集群外部的请求，则通过<code>docker0</code>网桥设备充当的网关，同时通过iptables做地址转换。我们会发现，这其实就是对当个容器的bridge网络模型的扩展。</p>
<h2 id="主流kubernetes网络方案"><a href="#主流kubernetes网络方案" class="headerlink" title="主流kubernetes网络方案"></a>主流kubernetes网络方案</h2><p>上一小节我们知道单个pod的网络模型是容器网络模型的扩展，但是pod与pod之间的是怎么相互通信的呢？这其实与容器之间相互通信非常类似，也分为同一个主机上的pod之间与跨主机的pod之间两种。</p>
<p>如容器网络模型一样，对于统一主机上的pod之间，通过<code>docker0</code>网桥设备直接二层（数据链路层）网络上通过MAC地址直接通信：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003388890.gif" alt="img"></p>
<p>而跨主机的pod之间的相互通信也主要有以下两个思路：</p>
<ol>
<li><p>修改底层网络设备配置，加入容器网络IP地址的管理，修改路由器网关等，该方式主要和SDN(Software define networking)结合。</p>
</li>
<li><p>完全不修改底层网络设备配置，复用原有的underlay平面网络，解决容器跨主机通信，主要有如下两种方式:</p>
</li>
<li><ul>
<li>隧道传输(Overlay)：将容器的数据包封装到原主机网络的三层或者四层数据包中，然后使用主机网络的IP或者TCP/UDP传输到目标主机，目标主机拆包后再转发给目标容器。Overlay隧道传输常见方案包括Vxlan、ipip等，目前使用Overlay隧道传输技术的主流容器网络有Flannel等；</li>
</ul>
</li>
</ol>
<p><img src="http://iubest.gitee.io/pic/640-1601003388890.gif" alt="img"></p>
<ul>
<li>修改主机路由：把容器网络加到主机路由表中，把主机网络设备当作容器网关，通过路由规则转发到指定的主机，实现容器的三层互通。目前通过路由技术实现容器跨主机通信的网络如Flannel host-gw、Calico等；</li>
</ul>
<p><img src="http://iubest.gitee.io/pic/640-1601003388889.gif" alt="img"></p>
<p>下面简单介绍几种主流的方案：</p>
<ul>
<li>Flannel是目前使用最为普遍的方案，提供了多种网络backend，它支持多种数据路径，也适合于overlay/underlay等多种场景。对于overlay的数据包封装，可以使用用户态的UDP，内核态的Vxlan(性能相对较好)，甚至在集群规模不大，且处于同一个二层域时可以采用host-gw的方式修改主机路由表；</li>
<li>Weave工作模式与Flannel很相似的，它最早只提供了UDP（称为sleeve模式）的网络方式，后来又加上了fastpass方式（基于VxLAN），不过Weave消除了Flannel中用来存储网络地址的额外组件，自己集成了高可用的数据存储功能；</li>
<li>Calico主要是采用了修改主机路由，节点之间采用BGP的协议去进行路由的同步。但是现实中的网络并不总是支持BGP路由的，因此Calico也支持内核中的IPIP模式，使用overlay的方式来传输数据；</li>
</ul>
<h2 id="策略控制-Network-Policy"><a href="#策略控制-Network-Policy" class="headerlink" title="策略控制(Network Policy)"></a>策略控制(Network Policy)</h2><p>Network Policy)是Kubernetes提供的基于策略的网络控制，用于隔离应用并提高安全性。它使用Kubernetes中常用的标签选择器模拟传统的分段网络，并通过策略控制它们之间的东西流量以及与外部交流的南北流量。</p>
<blockquote>
<p>Note: 确保使用的网络插件支持策略控制(Network Policy)，比如Flannel就没有实现Network Policy；</p>
</blockquote>
<p>下面的例子是配置一个典型的Network Policy的实例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">apiVersion: networking.k8s.io&#x2F;v1</span><br><span class="line">kind: NetworkPolicy</span><br><span class="line">metadata:</span><br><span class="line">  name: test-network-policy</span><br><span class="line">  namespace: default</span><br><span class="line">spec:</span><br><span class="line">  podSelector:</span><br><span class="line">    matchLabels:</span><br><span class="line">      role: db</span><br><span class="line">  policyTypes:</span><br><span class="line">  - Ingress</span><br><span class="line">  - Egress</span><br><span class="line">  ingress:</span><br><span class="line">  - from:</span><br><span class="line">    - ipBlock:</span><br><span class="line">        cidr: 172.17.0.0&#x2F;16</span><br><span class="line">        except:</span><br><span class="line">        - 172.17.1.0&#x2F;24</span><br><span class="line">    - namespaceSelector:</span><br><span class="line">        matchLabels:</span><br><span class="line">          project: myproject</span><br><span class="line">    - podSelector:</span><br><span class="line">        matchLabels:</span><br><span class="line">          role: frontend</span><br><span class="line">    ports:</span><br><span class="line">    - protocol: TCP</span><br><span class="line">      port: 6379</span><br><span class="line">  egress:</span><br><span class="line">  - to:</span><br><span class="line">    - ipBlock:</span><br><span class="line">        cidr: 10.0.0.0&#x2F;24</span><br><span class="line">    ports:</span><br><span class="line">    - protocol: TCP</span><br><span class="line">      port: 5978</span><br></pre></td></tr></table></figure>

<p>它使用标签选择器<code>namespaceSelector</code>与<code>posSelector</code>控制pod之间的流量，流量的行为模式主要由以下三个对象决定：</p>
<ol>
<li>控制对象：通过<code>spec.podSelector</code>筛选</li>
<li>流量方向：<code>ingress</code>控制入pod流量，<code>egress</code>控制出pod流量</li>
<li>流量特征：对端-IP-协议-端口</li>
</ol>
<p>通过使用Network Policy可以实现对进出流的精确控制，它采用各种选择器（标签或namespace），找到一组满足条件的pod，或者找到相当于通信的两端，然后通过流量的特征描述来决定它们之间是不是可以连通，可以理解为一个白名单的机制。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/浅谈 Yaml 的百般武艺"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/%E6%B5%85%E8%B0%88%20Yaml%20%E7%9A%84%E7%99%BE%E8%88%AC%E6%AD%A6%E8%89%BA/"
    >浅谈 Yaml 的百般武艺.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/%E6%B5%85%E8%B0%88%20Yaml%20%E7%9A%84%E7%99%BE%E8%88%AC%E6%AD%A6%E8%89%BA/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="浅谈-Yaml-的百般武艺"><a href="#浅谈-Yaml-的百般武艺" class="headerlink" title="浅谈 Yaml 的百般武艺"></a>浅谈 Yaml 的百般武艺</h1><p>在 Kubernetes（简称K8s)中，所有的对象都称之为资源，如 Pod 资源、RC副本控制器、Deployment 资源、Service 资源等，K8s通过Yaml配置文件进行便捷、高效的资源管理，如创建资源、调整资源等。<strong>G行经过大量实践总结，建立了一套成体系的容器云平台建设解决方案，基于Yaml文件本身的优异性，约定统一的人机交互方式。</strong>通过Yaml文件的修改，加载操作，统合其他资源，完成服务的发布，提供业务支撑，日常运维等工作。</p>
<blockquote>
<p>本文结合 G 行容器云平台运维情况，对Yaml文件在容器编排管理方面作用及驱动资源处理流程，Yaml的资源定义以及常见的Yaml文件编写方式进行了分析总结。</p>
</blockquote>
<h2 id="一、Yaml-文件对容器云平台运维意义"><a href="#一、Yaml-文件对容器云平台运维意义" class="headerlink" title="一、Yaml 文件对容器云平台运维意义"></a>一、Yaml 文件对容器云平台运维意义</h2><p>大家日常都会去逛超市，在超市我们消费各式各样的商品，最后到柜台结账时，可能发现超支了或者忘买了一些东西，那么我们如何准确核对自己的消费商品呢？大家可能已经想到我们可以凭借消费小票查看我们购买了哪些商品，还缺少哪些商品。</p>
<p>对于K8s来说，Yaml文件就可以看作我们的消费小票，它控制我们可以消费多少资源，甚至在Yaml文件里我们可以约定端口，配置监控规则，加入健康检查，挂载卷组资源等等，这些和运维相关的资源，配置，网络，功能等编定与控制，都可以通过修改Yaml文件的方式进行调整控制，可以说<strong>Yaml文件是运维人员扣动容器化这把武器的扳机。</strong></p>
<p>Yaml文件运维模式的建立，对K8s的定义带来极大好处。其便携性，能够让编写过程中不必添加大量的参数到命令行中执行命令。其高维护性，让Yaml文件可通过源头控制，跟踪每次操作。其灵活性，可让Yaml创建比命令行更加复杂的结构。</p>
<p>Yaml文件如此重要，我们会很好奇，在一个完整的K8s体系内，Yaml文件是如何实现其被赋予的职责的呢？下面以一个Pod的创建为例，对Yaml文件的控制数据流进行介绍。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601167881907.png" alt="img"></p>
<p>在K8s集群内，每个Node节点上都会启用一个 Kubelet 服务进程，该进程用于处理 Master下发到本节点的任务。每个 Kubelet 服务进程会在 APIServer 上注册节点自身信息并定期向 Master 节点汇报节点资源使用情况。</p>
<p>Scheduler做为调度器，起到了承上启下的作用。承上是指其接收新创建的Pod并为其安排一个落脚的“家”，启下是指完成安置工作后，目标Node上的Kubelet服务进程会接管后续工作。</p>
<p>当用户修改Yaml文件的配置信息进行新建Pod操作时，kubectl命令读取Yaml文件，并发送API请求到APIServer，APIServer交互更新数据到ETCD进行保存，同时Scheduler检测到资源请求，按照特定的调度算法和调度策略绑定到集群中某个适合的Node节点。此节点的Kubelet服务接收到该任务，拉取Yaml文件中指定的镜像完成Pod创建，同时更新“家”的信息到ETCD进行保存。</p>
<p>通过这个例子可以发现，用户除了对Yaml文件的修改无需进行其他操作，K8s集群会自动完成资源分配，资源下发，服务运行等等操作，极大地减少运维成本。</p>
<h2 id="二、Yaml文件的资源定义及驱动"><a href="#二、Yaml文件的资源定义及驱动" class="headerlink" title="二、Yaml文件的资源定义及驱动"></a>二、Yaml文件的资源定义及驱动</h2><p>我们可以通过Yaml文件控制系统在K8s集群的运行资源。对应用资源的副本数量、镜像、滚动升级策略、存活性、可用性、环境变量等进行约束。以下列举的几大类的Yaml模块编写范例。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860311.png" alt="img"></p>
<p><strong>1）存储资源的定义</strong></p>
<p>Yaml文件中的volumes模块用来定义存储卷组。在《K8s权威简书》中列举了11种volume类。我们通常使用persistentVolumeClaim来定义业务系统的存储卷，并默认放在Deployment中，这种模式可以让我们从网路存储（NAS）中申请资源，这样可以让我们做到数据与服务的分离，并提高资源的利用率。同时根据不同组件特性，可以结合使用secret和configMap类。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860184.png" alt="img"></p>
<p><strong>2）CPU，MEM资源的请求值，限定值</strong></p>
<p>如下图其中limits表示该类资源的上限值，requests表示建立后的初始值，requests不能够超过limits值。这一点在我们日常运维的扩缩容中需要关注。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860269.png" alt="img"></p>
<p><strong>3）网络资源的内外部路由配置</strong></p>
<p>我们通常在Service.Yaml里进行配置，并建议使用ClusterIP（L7）和LoadBalancer（L4）两种类型，同时建议Selector绑定内部路由的deployment的label。</p>
<p><strong>4）镜像资源的配置</strong>，下图是某个Deployment的镜像资源配置范例。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860229.png" alt="img"></p>
<p><strong>5）弹性伸缩功能的配置</strong></p>
<p>用户通过修改不同资源的Yaml文件中的-replicas参数的数量，来完成Pod的弹性伸缩。</p>
<p><strong>6）健康检查配置</strong></p>
<p>K8s中的健康检查分为就绪检查（readinessProbe），存活检查（livenessProbe）。针对不同健康检查，K8s提供httpGet（httpget检测），tcpSocket（tcp检测）机制，通过httpGet或tcpSocket检测，实现组件就绪及存活状态的检查功能。下图是存活检查的Yaml文件配置范例。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860270.png" alt="img"></p>
<h2 id="三、如何通过-Yaml-文件驱动-K8s-资源分配"><a href="#三、如何通过-Yaml-文件驱动-K8s-资源分配" class="headerlink" title="三、如何通过 Yaml 文件驱动 K8s 资源分配"></a>三、如何通过 Yaml 文件驱动 K8s 资源分配</h2><p>上面详细描述如何通过 Yaml 文件实现存储资源、计算资源、网络资源、应用资源的控制和调整，下面将结合 G行容器云平台运维经验以及运维过程中碰到的“坑”，给出如何通过 Yaml 文件驱动 K8s 资源供给、资源控制及资源创建。</p>
<p><strong>1、使用 Yaml 文件创建 Deployment 实例</strong></p>
<p>Pod 单独创建后如果出现故障，上面运行的容器应用也会随之宕掉。故提出了Deployment 的概念，其目的是让 Kubernetes 管理一组 Pod 的副本集，可保证始终运行固定数量的副本数。不会因为一个Pod挂掉导致整个服务不可使用。这里我们需要关注一个重要参数，避免踩雷。InitialDelaySeconds，这个参数归属于 Deployment 的健康检查模块，作用是约定容器启动完成后首次的探测时间。如果Deployment拉去镜像时间过长而这个值设置的时间过短，会导致pod的无限重启。因而应结合现场实际情况进行设置。创建 Deployment 实例：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601167895434.png" alt="img"></p>
<p>Deployment.Yaml 文件编写范式如下：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860312.png" alt="img"></p>
<p><strong>2、使用 Yaml 文件创建 Service 实例</strong></p>
<p>在K8s集群里，虽然每个Pod都会分配一个IP，但这个IP是一个虚化的概念，它会随着Pod的销毁而消失。这样随之而来了一个问题，如果一组Pod组成一个集群来提供服务，那么如何来访问他们呢？</p>
<p>Kubernetes 的 Service（服务）就是用来解决这个问题的。一个Service可以看做一组提供相同服务的 Pod 的对外访问接口。这里我们需要关注一个问题，一般情况下通过RC或者 Deployment 创建的 Pod 才可以使用 Service 绑定，而以 kind=Pod 的方式创建，Pod重建后Service也会变得无效。</p>
<p>其 Yaml 编写范式如下</p>
<p><img src="http://iubest.gitee.io/pic/640-1601167900925.png" alt="img"></p>
<p><strong>3、用Yaml文件创建Ingress实例</strong></p>
<p>通常情况下Service和Pod仅可在集群内部网络中通过IP地址访问。所有达到边界路由器的流量或被丢弃，或被转发。而Ingress可以提供外部可访问的URL，负载均衡，SSL等。用户可通过POST-Ingress资源到APIServer的方式来请求Ingress。其Yaml的编写范式如下：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003860473.png" alt="img"></p>
<h2 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h2><p>在 Kubernetes 中，一切皆资源，一切资源都可以通过 Yaml 文件来定义，Yaml 文件编写质量、标准化程度等直接影响容器云平台运行情况，G行开发、运维人员一起结合G行系统运行特点及运维要求，对 Yaml 编写规范进行梳理、对 Yaml 使用进行约束、对Yaml 配置项进行定义，通过对 Yaml 编写逐步规范，提升 Yaml 质量，发挥 Yaml 在 Kubernetes 中资源管理的价值，提升容器云平台运行稳定性，助力 G 行分布式微服务转型。</p>
<p>来源：本文转自公众号 匠心独运维妙维效，<a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzI1MTA5NDg5Ng==&mid=2647785030&idx=1&sn=45060ae149f44c922ffc7d05cc8f65c0&scene=21#wechat_redirect">点击查看原文</a>。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-k8s/用了3年Kubernetes，我们得到的5个教训"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/k8s/%E7%94%A8%E4%BA%863%E5%B9%B4Kubernetes%EF%BC%8C%E6%88%91%E4%BB%AC%E5%BE%97%E5%88%B0%E7%9A%845%E4%B8%AA%E6%95%99%E8%AE%AD/"
    >用了3年Kubernetes，我们得到的5个教训.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/k8s/%E7%94%A8%E4%BA%863%E5%B9%B4Kubernetes%EF%BC%8C%E6%88%91%E4%BB%AC%E5%BE%97%E5%88%B0%E7%9A%845%E4%B8%AA%E6%95%99%E8%AE%AD/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/k8s/">k8s</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="用了3年Kubernetes，我们得到的5个教训"><a href="#用了3年Kubernetes，我们得到的5个教训" class="headerlink" title="用了3年Kubernetes，我们得到的5个教训"></a>用了3年Kubernetes，我们得到的5个教训</h1><p>我们从 2017 年开始基于 1.9.4 版本构建第一个 Kubernetes 集群。我们有两个集群，一个集群在裸机的 RHEL 虚拟机上运行，另一个集群在 AWS EC2 上运行。</p>
<p>现在，我们的 Kubernetes 基础设施平台由分布在多个数据中心的 400 多台虚拟机组成。该平台托管了高可用的关键任务软件应用程序和系统，以管理具有近四百万个活动设备的大型实时网络。</p>
<p>Kubernetes 最终使我们变得更轻松，但是这个过程很艰难，是一种思维上的转变。不仅让我们的技能和工具有了彻底的转变，还让我们的设计和思维也得到了彻底的转变。我们不得不采用多种新技术，并进行大量投资以扩展和提高我们的团队和基础架构的技能。</p>
<p>回顾 Kubernetes 在生产环境中运行的这三年，我们记下了一些很重要的经验教训。</p>
<p>1. Java 应用程序的奇怪案例</p>
<p>在微服务和容器化方面，工程师倾向于避免使用 Java，这主要是由于 Java 臭名昭著的内存管理。但是，现在情况发生了改变，过去几年来 Java 的容器兼容性得到了改善。毕竟，大量的系统（例如<code>Apache Kafka</code>和<code>Elasticsearch</code>）在 Java 上运行。</p>
<p>回顾 2017-18 年度，我们有一些应用程序在 Java 8 上运行。这些应用程序通常很难理解像 Docker 这样的容器环境，并因堆内存问题和异常的垃圾回收趋势而崩溃。我们了解到，这是由于 JVM 无法使用Linux<code>cgroup</code>和<code>namespace</code>造成的，而它们是容器化技术的核心。</p>
<p>但是，从那时起，Oracle 一直在不断提高 Java 在容器领域的兼容性。甚至 Java 8 的后续补丁都引入了实验性的 JVM 标志来解决这些问题，<code>XX:+UnlockExperimentalVMOptions</code>和<code>XX:+UseCGroupMemoryLimitForHeap</code>。</p>
<p>但是，尽管做了所有的这些改进，不可否认的是，Java 在内存占用方面仍然声誉不佳，与 Python 或 Go 等同行相比启动速度慢。这主要是由 JVM 的内存管理和类加载器引起的。</p>
<p>现在，如果我们必须选择 Java，请确保版本为 11 或更高。并且 Kubernetes 的内存限制要在 JVM 最大堆内存（<code>-Xmx</code>）的基础上增加 1GB，以留有余量。也就是说，如果 JVM 使用 8GB 的堆内存，则我们对该应用程序的 Kubernetes 资源限制为 9GB。</p>
<p>2. Kubernetes 生命周期管理：升级</p>
<p>Kubernetes 生命周期管理（例如升级或增强）非常繁琐，尤其是如果已经在 裸金属或虚拟机 上构建了自己的集群。对于升级，我们已经意识到，最简单的方法是使用最新版本构建新集群，并将工作负载从旧版本过渡到新版本。节点原地升级所做的努力和计划是不值得的。</p>
<p>Kubernetes 具有多个活动组件，需要升级保持一致。从 Docker 到 Calico 或 Flannel 之类的 CNI 插件，你需要仔细地将它们组合在一起才能正常工作。虽然像 Kubespray、Kubeone、Kops 和 Kubeaws 这样的项目使它变得更容易，但它们都有缺点。</p>
<p>我们在 RHEL 虚拟机上使用 Kubespray 构建了自己的集群。Kubespray 非常棒，它具有用于构建、添加和删除新节点、升级版本的 playbook，以及我们在生产环境中操作 Kubernetes 所需的几乎所有内容。但是，用于升级的 playbook 附带了免责声明，以避免我们跳过子版本。因此，必须经过所有中间版本才能到达目标版本。</p>
<p>关键是，如果你打算使用 Kubernetes 或已经在使用 Kubernetes，请考虑生命周期活动以及解决这一问题的方案。构建和运行集群相对容易一些，但是生命周期维护是一个全新的体验，具有多个活动组件。</p>
<p>3. 构建和部署</p>
<p>在准备重新设计整个构建和部署流水线之前， 我们的构建过程和部署必须经历 Kubernetes 世界的完整转型。不仅在 Jenkins 流水线中进行了大量的重构，而且还使用了诸如 Helm 之类的新工具，策划了新的 git 流和构建、标签化 docker 镜像，以及版本化 helm 的部署 chart。</p>
<p>你需要一种策略来维护代码，以及 Kubernetes 部署文件、Docker 文件、Docker 镜像、Helm chart，并设计一种方法将它们组合在一起。</p>
<p>经过几次迭代，我们决定采用以下设计。</p>
<ul>
<li><p>应用程序代码及其 helm chart 放在各自的 git 存储库中。这使我们可以分别对它们进行版本控制（语义版本控制）。</p>
</li>
<li><p>然后，我们将 chart 版本与应用程序版本关联起来，并使用它来跟踪发布。例如，<code>app-1.2.0</code>使用<code>charts-1.1.0</code>进行部署。如果只更改 Helm 的 values 文件，则只更改 chart 的补丁版本（例如，从<code>1.1.0</code>到<code>1.1.1</code>）。所有这些版本均由每个存储库中的<code>RELEASE.txt</code>中的发行说明规定。</p>
</li>
<li><p>对于我们未构建或修改代码的系统应用程序，例如 Apache Kafka 或 Redis ，工作方式有所不同。也就是说，我们没有两个 git 存储库，因为 Docker 标签只是 Helm chart 版本控制的一部分。如果我们更改了 docker 标签以进行升级，则会升级 chart 标签的主要版本。</p>
</li>
</ul>
<p>4. 存活和就绪探针（双刃剑）</p>
<p>Kubernetes 的存活探针和就绪探针是自动解决系统问题的出色功能。它们可以在发生故障时重启容器，并将流量从不正常的实例进行转移。但是，在某些故障情况下，这些探针可能会变成一把双刃剑，并会影响应用程序的启动和恢复，尤其是有状态的应用程序，例如消息平台或数据库。</p>
<p>我们的 Kafka 系统就是这个受害者。我们运行了一个<code>3 Broker 3 Zookeeper</code>有状态副本集，该状态集的<code>ReplicationFactor</code>为 3，<code>minInSyncReplica</code>为 2。当系统意外故障或崩溃导致 Kafka 启动时，问题发生了。这导致它在启动期间运行其他脚本来修复损坏的索引，根据严重性，此过程可能需要 10 到 30 分钟。由于增加了时间，存活探针将不断失败，从而向 Kafka 发出终止信号以重新启动。这阻止了 Kafka 修复索引并完全启动。</p>
<p>唯一的解决方案是在存活探针设置中配置<code>initialDelaySeconds</code>，以在容器启动后延迟探针评估。但是，问题在于很难对此加以评估。有些恢复甚至需要一个小时，因此我们需要提供足够的空间来解决这一问题。但是，<code>initialDelaySeconds</code>越大，弹性的速度就越慢，因为在启动失败期间 Kubernetes 需要更长的时间来重启容器。</p>
<p>因此，折中的方案是评估<code>initialDelaySeconds</code>字段的值，以在 Kubernetes 中的弹性与应用程序在所有故障情况（磁盘故障、网络故障、系统崩溃等）下成功启动所花费的时间之间取得更好的平衡 。</p>
<blockquote>
<p>更新：如果你使用最新版本，Kubernetes 引入了第三种探针类型，称为“启动探针”，以解决此问题。从 1.16 版开始提供 alpha 版本，从 1.18 版开始提供 beta 版本。</p>
<p>启动探针会禁用就绪和存活检查，直到容器启动为止，以确保应用程序的启动不会中断。</p>
</blockquote>
<p>5. 公开外部 IP</p>
<p>我们了解到，使用静态外部 IP 公开服务会对内核的连接跟踪机制造成巨大代价。除非进行完整的计划，否则它很轻易就破坏了扩展性。</p>
<p>我们的集群运行在<code>Calico for CNI</code>上，在 Kubernetes 内部采用<code>BGP</code>作为路由协议，并与边缘路由器对等。对于 Kubeproxy，我们使用<code>IP Tables</code>模式。我们在 Kubernetes 中托管着大量的服务，通过外部 IP 公开，每天处理数百万个连接。由于来自软件定义网络的所有 SNAT 和伪装，Kubernetes 需要一种机制来跟踪所有这些逻辑流。为此，它使用内核的<code>Conntrack and netfilter</code>工具来管理静态 IP 的这些外部连接，然后将其转换为内部服务 IP，然后转换为 pod IP。所有这些都是通过<code>conntrack</code>表和 IP 表完成的。</p>
<p>但是<code>conntrack</code>表有其局限性。一旦达到限制，你的 Kubernetes 集群（如下所示的 OS 内核）将不再接受任何新连接。在 RHEL 上，可以通过这种方式进行检查。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#96;$  sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count &#x3D; 167012&#96;&#96;net.netfilter.nf_conntrack_max &#x3D; 262144&#96;</span><br></pre></td></tr></table></figure>

<p>解决此问题的一些方法是使用边缘路由器对等多个节点，以使连接到静态 IP 的传入连接遍及整个集群。因此，如果你的集群中有大量的计算机，累积起来，你可以拥有一个巨大的<code>conntrack</code>表来处理大量的传入连接。</p>
<p>回到 2017 年我们刚开始的时候，这一切就让我们望而却步，但最近，Calico 在 2019 年对此进行了详细研究，标题为“为什么 conntrack 不再是你的朋友”。</p>
<p>你是否一定需要 Kubernetes ？</p>
<p>三年过去了，我们每天仍然在继续发现和学习新知识。它是一个复杂的平台，具有自己的一系列挑战，尤其是在构建和维护环境方面的开销。它将改变你的设计、思维、架构，并需要提高技能和扩大团队规模以适应转型。</p>
<p>但是，如果你在云上并且能够将 Kubernetes 作为一种“服务”使用，它可以减轻平台维护带来的大部分开销，例如“如何扩展内部网络 CIDR？”或“如何升级我的 Kubernetes 版本？”</p>
<p>今天，我们意识到，你需要问自己的第一个问题是“你是否一定需要 Kubernetes？”。这可以帮助你评估所遇到的问题以及 Kubernetes 解决该问题的重要性。</p>
<p>Kubernetes 转型并不便宜，为此支付的价格必须确实证明“你的”用例的必要性及其如何利用该平台。如果可以，那么 Kubernetes 可以极大地提高你的生产力。</p>
<p>记住，为了技术而技术是没有意义的。</p>
<p>原文链接：</p>
<p><a target="_blank" rel="noopener" href="https://medium.com/better-programming/3-years-of-kubernetes-in-production-heres-what-we-learned-44e77e1749c8">https://medium.com/better-programming/3-years-of-kubernetes-in-production-heres-what-we-learned-44e77e1749c8</a></p>
<hr>
<p><strong>InfoQ Pro</strong> 是 InfoQ 专为技术早期开拓者和乐于钻研的技术探险者打造的专业媒体服务平台。扫描下方二维码关注 InfoQ Pro<strong>，</strong>即可在【充电计划】中获取<strong>技术 PPT 下载链接，**</strong>每周更新哟~**持续关注我们，还有更多技术分享活动与干货资料，就等你来！  </p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/YriaiaJPb26VM7tTGD3udSxOHB93Y5FjTYEz9NqllHxqRCqyGu6Qia0gwQ2Cv42P94pGsPmO9f1R6R1DG3Eu5GgMA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>点个在看少个 bug👇</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/k8s/" rel="tag">k8s</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-linux/17条实用Linux命令"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/linux/17%E6%9D%A1%E5%AE%9E%E7%94%A8Linux%E5%91%BD%E4%BB%A4/"
    >17条实用Linux命令.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/17%E6%9D%A1%E5%AE%9E%E7%94%A8Linux%E5%91%BD%E4%BB%A4/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="17条实用Linux命令"><a href="#17条实用Linux命令" class="headerlink" title="17条实用Linux命令"></a>17条实用Linux命令</h1><p>\1. 按oracleSID来查询连接数据库的客户端IP</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -anpT | grep oracleSID | awk &#39;&#123;print $5&#125;&#39; | grep -o -E  &#39;1.*:&#39; | awk -F &#39;:&#39; &#39;&#123;print $1&#125;&#39; | sort</span><br></pre></td></tr></table></figure>



<p>\2. 按监听端口1521来查询连接数据库的客户端IP</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -anpT | grep 1521 | awk &#39;&#123;print $5&#125;&#39; | grep -o -E  &#39;1.*:&#39; | awk -F &#39;:&#39; &#39;&#123;print $1&#125;&#39; | sort</span><br></pre></td></tr></table></figure>



<p>\3. 杀掉数据库服务器上连接数据库oracleSID的客户端的进程</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kill -9 &#96;ps -ef | grep oracleSID | grep LOCAL&#x3D;NO | grep -v grep | awk &#39;&#123;print $2&#125;&#39;&#96;</span><br></pre></td></tr></table></figure>



<p>\4. 删掉某个用户userA下面的所有进程</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pkill -9 -u userA</span><br></pre></td></tr></table></figure>



<p>\5. 查询连接1521端口的process进程数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -pan |grep 1521 |wc -l</span><br></pre></td></tr></table></figure>



<p>\6. 查询来自某台服务器192.168.21.15的连接的process进程数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -pan |grep  192.168.21.15 |wc -l</span><br></pre></td></tr></table></figure>



<p>\7. 汇总连接1521端口的每个客户端IP的连接数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -apnT|grep 1521 |awk  &#39;&#123;print $5&#125;&#39;|sort -u |grep -v 1521 |grep -v &#39;*&#39; |awk -F &#39;:&#39;  &#39;&#123;print $4&#125;&#39;|uniq -c |sort -nrnetstat -anpT|grep 1521 |awk  &#39;&#123;print $5&#125;&#39;|grep -o -E  &#39;1.*:&#39; |awk -F &#39;:&#39; &#39;&#123;print $1&#125;&#39; | sort |uniq -c |sort -nr</span><br></pre></td></tr></table></figure>



<p>\8. 显示前10个占用空间最大的文件或目录 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">du -s * | sort -nr | head</span><br></pre></td></tr></table></figure>



<p>\9. 统计某一天的所有文件的总量大小 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls --full-time &#96;find .&#x2F;* -name &quot;log_*.bak&quot;&#96; | grep &#39;2016-05-09&#39; | awk &#39;&#123;print $9&#125;&#39; | xargs du -ck</span><br></pre></td></tr></table></figure>



<p>\10. 删除几天前的文件 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">find &#x2F;mitac&#x2F;mds&#x2F;arch&#x2F; -ctime +150 -exec rm -rf &#123;&#125; \;--常用 find &#x2F;mitac&#x2F;mds&#x2F;arch&#x2F; -name &#39;*836701255.dbf&#39; -ctime +150 -exec rm -rf &#123;&#125; \;--删除以836701255.dbf结尾，150天前被修改过的文件 </span><br></pre></td></tr></table></figure>



<p>\11. CPU占用最多的前10个进程： </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps auxw|head -1;ps auxw|sort -rn -k3|head -10 </span><br></pre></td></tr></table></figure>



<p>\12. 内存消耗最多的前10个进程 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps auxw|head -1;ps auxw|sort -rn -k4|head -10 </span><br></pre></td></tr></table></figure>



<p> \13. 虚拟内存使用最多的前10个进程 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps auxw|head -1;ps auxw|sort -rn -k5|head -10 top后按1，可以看到多少个cpu，按shift+p则按cpu排序，按shift+m则按内存排序</span><br></pre></td></tr></table></figure>



<p>\14. vi替换所有字符串，如下把1都换成2</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:%s&#x2F;1&#x2F;2&#x2F;g</span><br></pre></td></tr></table></figure>



<p>\15. 查看IO信息，如下 每1秒显示一次，一共显示3次，显示为MB</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iostat -d -x -m 1 3</span><br></pre></td></tr></table></figure>



<p>\16. 查看cp一个文件file1到/u01目录下需要多长时间</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">time cp file1 &#x2F;u01&#x2F;</span><br></pre></td></tr></table></figure>



<p>\17. 查看CPU某段时间比如7:00到9:00期间的使用率</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sar -s 07:00:00 -e 10:00:00</span><br></pre></td></tr></table></figure>



<p>原文链接：<a target="_blank" rel="noopener" href="http://blog.itpub.net/30126024/viewspace-2218406/">http://blog.itpub.net/30126024/viewspace-2218406/</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-linux/从 lsof 开始，深入理解 Linux 虚拟文件系统"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/linux/%E4%BB%8E%20lsof%20%E5%BC%80%E5%A7%8B%EF%BC%8C%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Linux%20%E8%99%9A%E6%8B%9F%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/"
    >从 lsof 开始，深入理解 Linux 虚拟文件系统.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/%E4%BB%8E%20lsof%20%E5%BC%80%E5%A7%8B%EF%BC%8C%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Linux%20%E8%99%9A%E6%8B%9F%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="从-lsof-开始，深入理解-Linux-虚拟文件系统"><a href="#从-lsof-开始，深入理解-Linux-虚拟文件系统" class="headerlink" title="从 lsof 开始，深入理解 Linux 虚拟文件系统"></a>从 lsof 开始，深入理解 Linux 虚拟文件系统</h2><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>有时会出现这样的情况，磁盘空间显示已经被占满，但是在查看磁盘的具体文件占用情况时，发现磁盘仍然有很大的空余空间。</p>
<p>1.执行<code>df</code>命令查看磁盘使用情况，发现磁盘已经满了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-bash-4.2$ df -ThFilesystem     Type      Size  Used Avail Use% Mounted on&#x2F;dev&#x2F;vda1      ext4       30G    30G 0      100% &#x2F;devtmpfs       devtmpfs  489M     0  489M   0% &#x2F;devtmpfs          tmpfs     497M     0  497M   0% &#x2F;dev&#x2F;shmtmpfs          tmpfs     497M   50M  447M  11% &#x2F;runtmpfs          tmpfs     497M     0  497M   0% &#x2F;sys&#x2F;fs&#x2F;cgroup</span><br></pre></td></tr></table></figure>

<p>2.执行 du 命令查看各个目录的磁盘占用情况，把各个目录文件的大小相加，发现并没有占满磁盘，有10多G空间莫名失踪。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-bash-4.2$ du -h --max-depth&#x3D;1 &#x2F;home16M &#x2F;home&#x2F;logs11G &#x2F;home&#x2F;serverdog11G &#x2F;home</span><br></pre></td></tr></table></figure>

<p>3.为何会出现这样的情况呢？</p>
<p>因为虽然文件已被删除，但是一些进程仍然打开这些文件，因此其占用的磁盘空间并没有被释放。执行<code>lsof</code> 命令显示打开已删除的文件。将有问题的进程重启（或，清空），磁盘空间就会得到释放。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-bash-4.2# lsof | grep deletemysqld     2470         mysql    4u      REG              253,1           0     523577 &#x2F;var&#x2F;tmp&#x2F;ibfTeQFn (deleted)mysqld     2470         mysql    5u      REG              253,1           0     523579 &#x2F;var&#x2F;tmp&#x2F;ibaHcIdW (deleted)mysqld     2470         mysql    6u      REG              253,1           0     523581 &#x2F;var&#x2F;tmp&#x2F;ibLjiALu (deleted)mysqld     2470         mysql    7u      REG              253,1           0     523585 &#x2F;var&#x2F;tmp&#x2F;ibCFnzTB (deleted)mysqld     2470         mysql   11u      REG              253,1           0     523587 &#x2F;var&#x2F;tmp&#x2F;ibCjuqva (deleted)</span><br></pre></td></tr></table></figure>

<p>那么，Linux 的文件系统，到底为什么这么设计呢？要了解这些，就要先弄清楚并不容易，下面将从一些基本概念入手，一步步将这些梳理清楚：</p>
<ul>
<li><p>什么是虚拟文件系统（VFS：virtual filesystem）？</p>
</li>
<li><p>什么是通用文件模型？</p>
</li>
<li><ul>
<li>超级块对象（superblock object）</li>
<li>索引节点对象（inode object）</li>
<li>文件对象（file object）</li>
<li>目录项对象（dentry object）</li>
<li>文件的概念</li>
</ul>
</li>
<li><p>文件的表达</p>
</li>
<li><ul>
<li>内存表达</li>
<li>磁盘表达</li>
</ul>
</li>
<li><p>目录树的构建</p>
</li>
<li><ul>
<li>软链接 vs 硬链接</li>
</ul>
</li>
<li><p>文件 &amp; 磁盘管理</p>
</li>
<li><ul>
<li>索引节点状态</li>
</ul>
</li>
<li><p>文件 &amp; 进程管理</p>
</li>
<li><ul>
<li><p>操作：</p>
<p>打开&amp;删除</p>
</li>
</ul>
</li>
</ul>
<h3 id="虚拟文件系统（virtual-filesystem）"><a href="#虚拟文件系统（virtual-filesystem）" class="headerlink" title="虚拟文件系统（virtual filesystem）"></a>虚拟文件系统（virtual filesystem）</h3><p>下图显示了 Linux 操作系统中负责文件管理的基本组件。上半区域为用户模式，下半区域为内核模式。应用程序使用标准库libc来访问文件，库将请求映射到系统调用，以便进入内核模式。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601001041500.webp" alt="img"></p>
<p>所有与文件相关的操作的入口都是虚拟文件系统（VFS），而非特定的额文件系统（如Ext3、ReiserFS和NFS）。VFS 提供了系统库和特定文件系统之间的接口。因此，VFS 不仅充当抽象层，而且实际上它提供了一个文件系统的基本实现，可以由不同的实现来使用和扩展。因此，要了解文件系统是如何工作的，就要先了解VFS 。</p>
<h3 id="通用文件模型"><a href="#通用文件模型" class="headerlink" title="通用文件模型"></a>通用文件模型</h3><p>VFS 的主要思想在于引入了一个通用文件模型（common file model）。通用文件模型由以下对象类型组成：</p>
<p><strong>超级块对象（superblock object）</strong></p>
<blockquote>
<p>内存：文件系统安装时创建，存放文件系统的有关信息<br>磁盘：对应于存放在磁盘上的文件系统控制块（filesystem control block）</p>
</blockquote>
<p><strong>索引节点对象（inode object）</strong></p>
<blockquote>
<p>内存：访问时创建，存放关于具体文件的一般信息（<code>inode 结构</code>）<br>磁盘：对应于存放在磁盘上的文件控制块（file control block）<br>每个索引节点对象都有一个索引节点号，唯一地标识文件系统的文件</p>
</blockquote>
<p><strong>文件对象（file object）</strong></p>
<blockquote>
<p>内存：打开文件时创建，存放 打开文件 与进程之间进行交互的有关信息（<code>file 结构</code>）<br>打开文件信息，仅当进程访问文件期间存在于内核内存中。</p>
</blockquote>
<p><strong>目录项对象（dentry object）</strong></p>
<blockquote>
<p>内存：目录项一旦被读入内存，VFS就会将其转换成<code>dentry 结构</code>的目录项对象<br>磁盘：特定文件系统以特定的方式存储在磁盘上<br>存放目录项（即，文件名称）与对应文件进行链接的有关信息</p>
</blockquote>
<h3 id="目录树"><a href="#目录树" class="headerlink" title="目录树"></a>目录树</h3><p>综合来说，Linux 的 根文件系统(system’s root filessystem) 是内核启动mount的第一个文件系统。内核代码映像文件保存在根文件系统中，而系统引导启动程序会在根文件系统挂载之后，从中把一些基本的初始化脚本和服务等加载到内存中去运行（文件系统和内核是完全独立的两个部分）。其他文件系统，则后续通过脚本或命令作为子文件系统安装在已安装文件系统的目录上，最终形成整个目录树。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">start_kernel 　　vfs_caches_init 　　　　mnt_init 　　　　　　init_rootfs     &#x2F;&#x2F; 注册rootfs文件系统　　　　　　init_mount_tree &#x2F;&#x2F; 挂载rootfs文件系统 　　… 　　rest_init 　　kernel_thread(kernel_init, NULL, CLONE_FS);</span><br></pre></td></tr></table></figure>

<p>就单个文件系统而言，在文件系统安装时，创建超级块对象；沿树查找文件时，总是首先从初识目录的中查找匹配的目录项，以便获取相应的索引节点，然后读取索引节点的目录文件，转化为dentry对象，再检查匹配的目录项，反复执行以上过程，直至找到对应的文件的索引节点，并创建索引节点对象。</p>
<h3 id="软链接-vs-硬链接"><a href="#软链接-vs-硬链接" class="headerlink" title="软链接 vs 硬链接"></a>软链接 vs 硬链接</h3><p>软链接是一个普通的文件，其中存放的是另外一个文件的路径名。硬链接则指向同一个索引节点，硬链接数记录在索引节点对象的 i_nlink 字段。当<code>i_nlink</code>字段为零时，说明没有硬链接指向该文件。</p>
<h2 id="文件-amp-进程管理"><a href="#文件-amp-进程管理" class="headerlink" title="文件 &amp; 进程管理"></a>文件 &amp; 进程管理</h2><p>下图是一个简单示例，说明进程是怎样与文件进行交互。三个不同进程打开同一个文件，每个进程都有自己的文件对象，其中两个进程使用同一个硬链接（每个硬链接对应一个目录对象），两个目录项对象都指向同一个 索引节点对象。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601001041603.webp" alt="img"></p>
<p>索引节点的数据又由两部分组成：内存数据和磁盘数据。Linux 使用 Write back 作为索引节点的数据一致性策略。对于索引节点的数据，当文件被打开时，才会加载索引节点到内存；当不再被进程使用，则从内存踢出；如果中间有更新，则需要把数据写回磁盘</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*  &quot;in_use&quot; - valid inode, i_count &gt; 0, i_nlink &gt; 0*  &quot;dirty&quot;  - as &quot;in_use&quot; but also dirty*  &quot;unused&quot; - valid inode, i_count &#x3D; 0</span><br></pre></td></tr></table></figure>

<p>索引节点是否仍在使用，是通过 <code>open()</code> 和 <code>close()</code> 操作建立和销毁文件对象，文件对象通过索引节点提供的 <code>iget</code> 和 <code>iput</code> 更新索引节点的i_count字段，以完成使用计数。open 操作使得 i_count 加一， close 操作使得 i_count 减一。在 close 操作时判断索引节点是否释放，如果 i_count = 0，则意味着不再有进程引用，将会从内存释放。</p>
<h3 id="文件-amp-磁盘管理"><a href="#文件-amp-磁盘管理" class="headerlink" title="文件 &amp; 磁盘管理"></a>文件 &amp; 磁盘管理</h3><p>文件与磁盘管理联系最紧密的操作，莫过于<code>touch</code>和<code>rm</code>操作，而尤以后者最为关键。通过strace(或 dtruss)，查看 rm 的实际的系统调用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># dtruss rm tmp...geteuid(0x0, 0x0, 0x0)       &#x3D; 0 0ioctl(0x0, 0x4004667A, 0x7FFEE06F09C4)       &#x3D; 0 0lstat64(&quot;tmp\0&quot;, 0x7FFEE06F0968, 0x0)        &#x3D; 0 0access(&quot;tmp\0&quot;, 0x2, 0x0)        &#x3D; 0 0unlink(&quot;tmp\0&quot;, 0x0, 0x0)        &#x3D; 0 0</span><br><span class="line">可以发现 rm 实际是通过 unlink 完成的。unlink代表删除目录项，以及减少其索引节点的计数。由通用文件模型可知，父目录本身同样是一个文件，也就意味着目录项是其文件数据的一部分。删除目录项等价于从父目录的文件中删除数据，也就意味着首先要打开父目录的文件。那么，删除操作即可理解为：</span><br></pre></td></tr></table></figure>

<ol>
<li><p>删除命令（一个进程）使用 open 操作获得父目录文件对象</p>
</li>
<li><p>通过 <code>iget</code> 增加 目录文件的索引节点对象计数</p>
</li>
<li><p>读取目录文件数据</p>
</li>
<li><ul>
<li>将目录文件数据转化为目录项对象</li>
<li>由于目录项包含文件的索引节点，类似的，需要通过 iget 增加文件的索引节点对象计数</li>
</ul>
</li>
<li><p>删除目录的目录项</p>
</li>
<li><p>减少文件索引节点对象的硬链接计数i_nlink</p>
</li>
<li><p>通过 <code>iput</code> 结束对文件索引节点对象的操作，使用计数 i_count 减一</p>
</li>
<li><ul>
<li>判断i_count是否为零，如果为零，则释放内存</li>
<li>然后，判断i_nlink是否为零，如果为零，则释放磁盘空间</li>
</ul>
</li>
<li><p>通过 iput 结束对目录索引节点对象的操作。</p>
</li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>回头来看遇到的问题，其实可以从两个角度来理解：</p>
<h3 id="索引与数据"><a href="#索引与数据" class="headerlink" title="索引与数据"></a>索引与数据</h3><p>文件系统与文件、磁盘管理与文件、进程管理与文件，最核心的都是文件的索引，而不是文件的数据。把数据和索引分开是理解文件系统的关键。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601001041809.webp" alt="img"></p>
<h3 id="缓存策略"><a href="#缓存策略" class="headerlink" title="缓存策略"></a>缓存策略</h3><p>由于操作系统使用 Write back 的策略，意味着只有先释放内存，才有可能释放磁盘。</p>
<h3 id="Why-lsof"><a href="#Why-lsof" class="headerlink" title="Why lsof ?"></a>Why lsof ?</h3><p>从上面的模型可以很清楚的理解，因为目录已经没有索引到文件了，但是打开文件还有索引到文件，所以不能立刻释放磁盘空间。</p>
<p>为什么 lsof 可以找到已删除未释放的文件呢？</p>
<p>lsof，顾名思义：list open files，该命令的原理就是查找打开文件的列表，因此可以找到已删除未释放的文件。</p>
<p>作者：cyningsun<br>链接：<a target="_blank" rel="noopener" href="https://juejin.im/post/6875110082724659213">https://juejin.im/post/6875110082724659213</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-linux/分享Linux内存占用几个案例"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/linux/%E5%88%86%E4%BA%ABLinux%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8%E5%87%A0%E4%B8%AA%E6%A1%88%E4%BE%8B/"
    >分享Linux内存占用几个案例.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/%E5%88%86%E4%BA%ABLinux%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8%E5%87%A0%E4%B8%AA%E6%A1%88%E4%BE%8B/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="分享Linux内存占用几个案例"><a href="#分享Linux内存占用几个案例" class="headerlink" title="分享Linux内存占用几个案例"></a>分享Linux内存占用几个案例</h1><h2 id="案例一"><a href="#案例一" class="headerlink" title="案例一"></a>案例一</h2><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>最近一台 <code>CentOS</code> 服务器，发现内存无端<code>损失</code>了许多，<code>free</code> 和 <code>ps</code> 统计的结果<code>相差十几个G</code>，非常奇怪，后来Google了许久才搞明白。</p>
<h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>1、linux系统内存消耗主要有三个地方：</p>
<ul>
<li>进程</li>
<li>slab</li>
<li>pagecacge</li>
</ul>
<p>用 <code>free</code> 命令查看到的是系统整体的内容使用情况，而使用 <code>ps</code> 和 <code>top</code> 看到的内存使用情况都是以进程维度来看的，因此看不到 <code>slabcache</code> <code>和pagecache</code> 的内存占用信息。</p>
<p>2、判断应用程序是否有内存泄露问题，只根据进程的内存使用或机器的内存变化来判定都不太准确，如果单凭进程的内存变化可能会疏忽一些小对象的内存泄露问题。</p>
<p>同时对于机器的内存的使用也要做是否合理的判断。对于不同语言的应用都有相应的神器可以辅助定位内存泄露问题，同时结合linux内存的监控工具进行分析， 除了 <code>top</code>，<code>free</code>还有 <code>pmap</code>，<code>/proc/meminfo</code> 和 <code>/slabinfo</code>，<code>slaptop</code>等。</p>
<p>3、通过这个问题，有一点比较重要的是，在使用监控工具进行监控和分析时，对其值的具体含义还是要了解的很清楚，否则会造成误判，使问题变得更加复杂。</p>
<p>4、此外 <code>page cache</code>，<code>dentries</code>和<code>inodes cache</code>，系统是会自动回收的。</p>
<p>可以通过以下几种方式加速其回收，不过实际并不需要这么做。</p>
<p>手工清除内存 缓存</p>
<ul>
<li><code>echo 1 &gt; /proc/sys/vm/drop_caches</code> 清除page cache</li>
<li><code>echo 2 &gt; /proc/sys/vm/drop_caches</code> 清除denries和inodes</li>
<li><code>echo 3 &gt; /proc/sys/vm/drop_caches</code> 清除page cache ，dentries及inodes</li>
<li>调整 <code>vm.vfs_cache_pressure</code> 值大小，默认是100。值越大，dentries和inodes cache的回收速度会越快</li>
<li>调整 <code>vm.min_free_kbytes</code> 值大小，该值为系统开始内存回收的阀值，越大表示系统会越早开始回收(一般情况下此值不建议调整)。</li>
</ul>
<h3 id="问题描述和初步调查"><a href="#问题描述和初步调查" class="headerlink" title="问题描述和初步调查"></a>问题描述和初步调查</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ free -g</span><br></pre></td></tr></table></figure>

<p><img src="http://iubest.gitee.io/pic/640-1601003514378.png" alt="img"></p>
<p>这台服务器有 <code>16G内存</code>，但是结果显示除了2G左右的文件Buffer缓存外，其余十几G都被确确实实的用光了。(free按1024进制计算，总内存可能比实际偏小)</p>
<p>这里大概介绍下 <code>free</code> 结果的含义:</p>
<p><img src="http://iubest.gitee.io/pic/640-1601003514424.png" alt="img"></p>
<p>然后 <code>top</code> 看了下，没有特别吃内存的程序。用 <code>ps</code> 大概统计下所有程序占用的总内存:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ echo &#96;ps aux |awk &#39;&#123;mem +&#x3D; $6&#125; END &#123;print mem&#x2F;1024&#x2F;1024&#125;&#39;&#96; GB</span><br><span class="line"></span><br><span class="line">0.595089</span><br></pre></td></tr></table></figure>

<p>结果显示所有进程占用的内存还不到 <code>1G</code>，实际上，因为<code>free</code>, <code>ps</code>的统计方式的差别和<code>Copy-on-write</code> 和 <code>Shared libraries</code>等内存优化机制的存在，这两者的统计结果通常是不一样的。但是一般情况下绝对不会相差十几个G，肯定是有什么隐藏的问题，Google了许久后发现，free没有专门统计另一项缓存: <code>Slab</code>。</p>
<h3 id="Slab-简介和进一步调查"><a href="#Slab-简介和进一步调查" class="headerlink" title="Slab 简介和进一步调查"></a>Slab 简介和进一步调查</h3><p><code>Slab Allocation</code> 是Kernel 2.2之后引入的一个内存管理机制，专门用于缓存内核的数据对象，可以理解为一个内核专用的对象池，可以提高系统性能并减少内存碎片。(Kernel 2.6.23之后，SLUB成为了默认的allocator)</p>
<h4 id="查看Slab缓存"><a href="#查看Slab缓存" class="headerlink" title="查看Slab缓存"></a>查看Slab缓存</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ cat &#x2F;proc&#x2F;meminfo</span><br><span class="line"></span><br><span class="line"># 其中，Slab相关的数据为</span><br><span class="line">Slab: 154212 kB</span><br><span class="line">SReclaimable: 87980 kB</span><br><span class="line">SUnreclaim: 66232 kB</span><br></pre></td></tr></table></figure>

<p>SReclaimable(Linux 2.6.19+) 都是 clean 的缓存，随时可以释放。回到之前的内存问题，</p>
<h4 id="查看服务器上Slab占用的内存："><a href="#查看服务器上Slab占用的内存：" class="headerlink" title="查看服务器上Slab占用的内存："></a>查看服务器上Slab占用的内存：</h4><p>方法1：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ cat &#x2F;proc&#x2F;meminfo|grep Slab</span><br><span class="line"></span><br><span class="line">Slab: 12777668 kB</span><br></pre></td></tr></table></figure>

<p>方法2：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">echo &#96;cat &#x2F;proc&#x2F;meminfo|grep Slab|awk &#39;&#123;mem +&#x3D; $2&#125; END &#123;print mem&#x2F;1024&#x2F;1024&#125;&#39;&#96; GB</span><br><span class="line"></span><br><span class="line">12G</span><br></pre></td></tr></table></figure>

<p><code>12G</code>的Slab缓存，有意思的是free把Slab缓存统计到了 <code>usedmemory</code> 中，这就是之前那个问题的症结所在了。另外，还可以查看 <code>/proc/slabinfo</code> (或使用 <code>slabtop</code> 命令)来查看Slab缓存的具体使用情况。</p>
<p>结果发现，<code>ext3_inode_cache</code> 和 <code>dentry_cache</code> 占用了绝大部分内存。考虑到这台服务器会频繁地用 <code>rsync</code> 同步大量的文件，这个结果也并不意外。</p>
<h3 id="解决问题"><a href="#解决问题" class="headerlink" title="解决问题"></a>解决问题</h3><p>先说明一下，如果问题仅仅是Slab占用了太多的内存(SReclaimable)，那么通常不需要太操心，因为这根本不是个问题(如果是SUnreclaim太多且不断增长，那么很有可能是内核有bug)。但是，如果是因为Slab占用内存太多而引起了其他的问题，建议继续往下阅读。</p>
<h4 id="清除Slab可回收缓存"><a href="#清除Slab可回收缓存" class="headerlink" title="清除Slab可回收缓存"></a>清除Slab可回收缓存</h4><p>通过 <code>/proc/sys/vm/drop_caches</code> 这个配置项，可以手动清除指定的可回收缓存(SReclaimable)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo 2 &gt; &#x2F;proc&#x2F;sys&#x2F;vm&#x2F;drop_caches&#96; 或者 &#96;sysctl vm.drop_caches&#x3D;2</span><br></pre></td></tr></table></figure>

<p>上面的命令会主动释放 <code>Slab</code> 中 <code>clean的缓存</code>(包括inode和dentry的缓存)，然后再 <code>free -g</code> 一下，空闲的内存陡增了十几个G</p>
<p><code>注意的是</code>，手动清除缓存可能会在一段时间内降低系统性能。原则上不推荐这么做，因为如果有需要，系统会自动释放出内存供其他程序使用。另外，手动清除Slab缓存是一个治标不治本的办法。因为问题不在Slab，实际操作的时候发现，清除缓存一段时间后，<code>Slab缓存</code>很快又会反弹回去。</p>
<p>如果需要治本，要么搞定问题进程，要么修改系统配置，要么增加物理内存。</p>
<h3 id="调整系统-vm-配置"><a href="#调整系统-vm-配置" class="headerlink" title="调整系统 vm 配置"></a>调整系统 vm 配置</h3><blockquote>
<p><code>风险预警</code>: 调整以下系统配置可能会对系统性能造成负面影响，请仔细测试并谨慎操作。</p>
</blockquote>
<p><code>/etc/sysctl.conf</code> 里有以下几个对内存管理影响比较大的内核参数配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vm.vfs_cache_pressure</span><br></pre></td></tr></table></figure>

<p>系统在进行内存回收时，会先回收page cache, inode cache, dentry cache和swap cache。vfs_cache_pressure 越大，每次回收时，inode cache和dentry cache所占比例越大。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vfs_cache_pressure</span><br></pre></td></tr></table></figure>

<p>默认是100，该值越大inode cache和dentry cache的回收速度会越快，越小则回收越慢，为0的时候完全不回收，内存溢出(OOM!)。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vm.min_free_kbytes</span><br></pre></td></tr></table></figure>

<p>系统的”保留内存”的大小，”保留内存”用于低内存状态下的”atomic memory allocation requests”(eg. kmalloc + GFP_ATOMIC)，该参数也被用于计算开始内存回收的阀值，默认在开机的时候根据当前的内存计算所得，越大则表示系统会越早开始内存回收。<code>vm.min_free_kbytes 过大可能会导致 OOM</code>，太小可能会导致系统出现死锁等问题。查看默认设置：<code>cat /proc/sys/vm/min_free_kbytes</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vm.swappiness</span><br></pre></td></tr></table></figure>

<p>该配置用于控制系统将内存 <code>swap out</code> 到交换空间的积极性，取值范围是[0, 100]。<code>vm.swappiness</code> 越大，系统的交换积极性越高，默认是60， 如果为0则不会进行交换。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ vim &#x2F;etc&#x2F;sysctl.conf</span><br><span class="line"></span><br><span class="line">vm.swappiness &#x3D; 1</span><br></pre></td></tr></table></figure>

<p>查看默认设置：<code>cat /proc/sys/vm/swappiness</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">drop_caches</span><br></pre></td></tr></table></figure>

<p>默认值为0，将此值设置为1，2或3，使内核删除页面缓存和slab缓存的各种组合。</p>
<ul>
<li>1 系统使所有的页面缓冲存储器失效并释放。</li>
<li>2 系统释放所有未使用的slab缓存内存。</li>
<li>3 系统释放所有的页面缓存和slab缓存内存。</li>
</ul>
<p>查看默认设置：<code>cat /proc/sys/vm/drop_caches</code></p>
<blockquote>
<p>注：以上参数设置是非破坏性的操作，由于脏数据不能被释放，建议设置以上参数的值之前运行sync把内存数据写入硬盘。在生产环境中不建议使用drop_caches释放内存。</p>
</blockquote>
<h2 id="案例二-（物理内存的外碎片回收）"><a href="#案例二-（物理内存的外碎片回收）" class="headerlink" title="案例二 （物理内存的外碎片回收）"></a>案例二 （物理内存的外碎片回收）</h2><h3 id="问题-1"><a href="#问题-1" class="headerlink" title="问题"></a>问题</h3><p>执行 <code>docker ps</code> 命令挂住</p>
<h3 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h3><p>查找 <code>/var/log/messages</code>，发现有错误信息：<code>XFS: possible memory allocation deadlock in kmem_alloc (mode:0x250)</code></p>
<p>在linux中使用<code>buddy算法</code>解决<code>物理内存</code>的<code>碎片问题</code>，其把所有空闲的内存，以2的幂次方的形式，分成11个块链表，分别对应为1、2、4、8、16、32、64、128、256、512、1024个页块。</p>
<p>Linux支持<code>NUMA技术</code>，对于NUMA设备，NUMA系统的结点通常是由一组CPU和本地内存组成，每一个节点都有相应的本地内存，因此buddyinfo 中的Node0表示节点ID；而每一个节点下的内存设备，又可以划分为多个内存区域(zone)，因此下面的显示中，对于Node0的内存，又划分类<code>DMA</code>、<code>Normal</code>、<code>HighMem</code>区域。而后面则是表示空闲的区域。</p>
<p>此处以 <code>Normal</code> 区域进行分析，第二列值为100，表示当前系统中normal区域，可用的连续两页的内存大小为100<em>2</em>PAGE_SIZE；第三列值为52，</p>
<p>表示当前系统中normal区域，可用的连续四页的内存大小为 <code>52*2^2* PAGE_SIZE</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ cat &#x2F;proc&#x2F;buddyinfo</span><br><span class="line"></span><br><span class="line">Node 0, zone DMA 23 15 4 5 2 3 3 2 3 1 0</span><br><span class="line">Node 0, zone Normal 149 100 52 33 23 5 32 8 12 2 59</span><br><span class="line">Node 0, zone HighMem 11 21 23 49 29 15 8 16 12 2 142</span><br></pre></td></tr></table></figure>

<p>可以看到从第5列开始，只剩下 <code>44*16*PAGE_SIZE</code> 的页块，后面剩下的分别是<code>1 * 32 *PAGE_SIZE</code>, <code>1 * 64 *PAGE_SIZE</code>, <code>2 *128 * PAGE_SIZE</code>，剩下256,512的页块都没有了因此推测，导致这个问题出现的时候，该机器的内存碎片很多，当某个应用执行时，在xfs的申请内存中有这种连续的大块的内存申请的操作的请求。</p>
<p>比如：6000: map = kmem_alloc(subnex * sizeof(*map), KM_MAYFAIL | KM_NOFS); 就会导致内存一直分配不到。</p>
<p>例如：执行 <code>docker ps</code>，<code>docker exec</code>这些命令时，会一直阻塞在 <code>kmem_alloc</code> 的循环中，反复申请内存，由于内存碎片没有被组合，因此就一直申请不到，执行这些命令也会卡住，这也就验证了执行某些命令如<code>ls</code>、<code>ssh</code>都不会失败(因为需要内存的 <code>PAGE</code> 不是那么大)。</p>
<h3 id="临时解决方法："><a href="#临时解决方法：" class="headerlink" title="临时解决方法："></a>临时解决方法：</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo 1 &gt; &#x2F;proc&#x2F;sys&#x2F;vm&#x2F;drop_caches</span><br></pre></td></tr></table></figure>

<p>上述命令操作会把碎片化的 <code>PAGE</code> 重新分配，之后在申请大块的PAGE内存就可以申请到，不阻塞了。</p>
<h3 id="永久解决方法："><a href="#永久解决方法：" class="headerlink" title="永久解决方法："></a>永久解决方法：</h3><p>优化内核参数：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 保留1G空闲内存，达到该值，触发内存回收</span><br><span class="line">$ cat &gt;&gt; &#x2F;etc&#x2F;sysctl.conf &lt;&lt;EOF</span><br><span class="line">vm.min_free_kbytes &#x3D; 1048576</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">#立即生效</span><br><span class="line">$ sysctl -p</span><br></pre></td></tr></table></figure>

<h3 id="什么是-overcommit-or-oom-问题"><a href="#什么是-overcommit-or-oom-问题" class="headerlink" title="什么是 overcommit or oom 问题"></a>什么是 overcommit or oom 问题</h3><p>Linux对大部分申请内存的请求都回复”yes”，以便能跑更多更大的程序，因为申请内存后，并不会马上使用内存，这种技术叫做 <code>Overcommit</code>。</p>
<p>当linux发现内存不足时，会发生OOM killer(OOM=out-of-memory)，它会选择杀死一些进程(用户态进程，不是内核线程)，以便释放内存。</p>
<p>当 oom-killer 发生时，linux会选择杀死哪些进程？选择进程的函数是oom_badness函数(在mm/oom_kill.c中)，该函数会计算每个进程的点数(0~1000)，点数越高，这个进程越有可能被杀死。</p>
<p>每个进程的点数跟 <code>oom_score_adj</code> 有关，而且 <code>oom_score_adj</code> 可以被设置(-1000最低，1000最高)。</p>
<h3 id="内核参数-overcommit-memory-它是内存分配策略"><a href="#内核参数-overcommit-memory-它是内存分配策略" class="headerlink" title="内核参数 overcommit_memory, 它是内存分配策略"></a>内核参数 overcommit_memory, 它是内存分配策略</h3><p><code>可选值</code>：0、1、2。</p>
<ul>
<li><code>0</code>：表示内核将检查是否有足够的可用内存供应用进程使用；如果有足够的可用内存，内存申请允许；否则，内存申请失败，并把错误返回给应用进程。</li>
<li><code>1</code>：表示内核允许分配所有的物理内存，而不管当前的内存状态如何。</li>
<li><code>2</code>：表示内核允许分配超过所有物理内存和交换空间总和的内存。</li>
</ul>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a target="_blank" rel="noopener" href="https://www.mawenbao.com/research/linux-ate-my-memory.html">https://www.mawenbao.com/research/linux-ate-my-memory.html</a></li>
<li><a target="_blank" rel="noopener" href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-memory-tunables">https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-memory-tunables</a></li>
<li><a target="_blank" rel="noopener" href="http://blog.51yip.com/nosql/1776.html">http://blog.51yip.com/nosql/1776.html</a></li>
<li><a target="_blank" rel="noopener" href="http://linuxperf.com/?p=102">http://linuxperf.com/?p=102</a></li>
</ul>
<blockquote>
<ul>
<li>案例一原文出处：<a target="_blank" rel="noopener" href="http://blog.atime.me/research/linux-ate-my-memory.html">http://blog.atime.me/research/linux-ate-my-memory.html</a></li>
<li>案例二原文出处：<a target="_blank" rel="noopener" href="https://www.cnblogs.com/beilong/p/8192676.html">https://www.cnblogs.com/beilong/p/8192676.html</a></li>
</ul>
</blockquote>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-linux/利用 Shell 脚本实现邮件监控 Linux 系统的内存"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/linux/%E5%88%A9%E7%94%A8%20Shell%20%E8%84%9A%E6%9C%AC%E5%AE%9E%E7%8E%B0%E9%82%AE%E4%BB%B6%E7%9B%91%E6%8E%A7%20Linux%20%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%86%85%E5%AD%98/"
    >利用 Shell 脚本实现邮件监控 Linux 系统的内存.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/%E5%88%A9%E7%94%A8%20Shell%20%E8%84%9A%E6%9C%AC%E5%AE%9E%E7%8E%B0%E9%82%AE%E4%BB%B6%E7%9B%91%E6%8E%A7%20Linux%20%E7%B3%BB%E7%BB%9F%E7%9A%84%E5%86%85%E5%AD%98/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="利用-Shell-脚本实现邮件监控-Linux-系统的内存"><a href="#利用-Shell-脚本实现邮件监控-Linux-系统的内存" class="headerlink" title="利用 Shell 脚本实现邮件监控 Linux 系统的内存"></a>利用 Shell 脚本实现邮件监控 Linux 系统的内存</h1><h4 id="一、安装Linux下面的一个邮件客户端Msmtp软件-类似于一个Foxmail的工具"><a href="#一、安装Linux下面的一个邮件客户端Msmtp软件-类似于一个Foxmail的工具" class="headerlink" title="一、安装Linux下面的一个邮件客户端Msmtp软件(类似于一个Foxmail的工具)"></a>一、安装Linux下面的一个邮件客户端Msmtp软件(类似于一个Foxmail的工具)</h4><p>1、下载安装：<a target="_blank" rel="noopener" href="http://downloads.sourceforge.net/">http://downloads.sourceforge.net</a> … 206451&amp;big_mirror=0</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># tar jxvf msmtp-1.4.16.tar.bz2  </span><br><span class="line"># cd msmtp-1.4.16  </span><br><span class="line"># .&#x2F;configure --prefix&#x3D;&#x2F;usr&#x2F;local&#x2F;msmtp  </span><br><span class="line"># make  </span><br><span class="line"># make install  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>2、创建msmtp配置文件和日志文件（host为邮件域名，邮件用户名test，密码123456）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"># vim ~&#x2F;.msmtprc  </span><br><span class="line">  </span><br><span class="line">account default   </span><br><span class="line">host 126.com   </span><br><span class="line">from test@126.com   </span><br><span class="line">auth login   </span><br><span class="line">user test   </span><br><span class="line">password 123456   </span><br><span class="line">logfile ~&#x2F;.msmtp.log  </span><br><span class="line">  </span><br><span class="line"># chmod 600 ~&#x2F;.msmtprc  </span><br><span class="line"># touch ~&#x2F;.msmtp.log  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>3、mutt安装配置：（一般linux下有默认安装mutt）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># vim ~&#x2F;.muttrc  </span><br><span class="line">  </span><br><span class="line">set sendmail&#x3D;&quot;&#x2F;usr&#x2F;local&#x2F;msmtp&#x2F;bin&#x2F;msmtp&quot;   </span><br><span class="line">set use_from&#x3D;yes   </span><br><span class="line">set real   </span><br><span class="line">set from&#x3D;test@126.com   </span><br><span class="line">set envelope_from&#x3D;yes   </span><br><span class="line">set rfc2047_parameters&#x3D;yes   </span><br><span class="line">set charset&#x3D;&quot;utf-8&quot; </span><br></pre></td></tr></table></figure>

<p>4、邮件发送测试（-s邮件标题，-a表加附件）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># echo &quot;邮件内容123456&quot; | mutt -s &quot;邮件标题测试邮件&quot; -a &#x2F;scripts&#x2F;test.txt test@126.com  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="二、使用Free命令监控系统内存"><a href="#二、使用Free命令监控系统内存" class="headerlink" title="二、使用Free命令监控系统内存"></a>二、使用Free命令监控系统内存</h4><p>1、使用free命令查看linux系统内存使用情况：（以M为单位）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># free -m   </span><br><span class="line">（执行结果如下样式）  </span><br><span class="line">total used free shared buffers cached  </span><br><span class="line">Mem: 3952 3414 538 0 168 484  </span><br><span class="line">-&#x2F;+ buffers&#x2F;cache: 2760 1191  </span><br><span class="line">Swap: 8191 86 8105  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>2、查看截取剩余内存free的数值命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(1) 物理内存free值：# free -m | grep Mem | awk &#39;&#123;print $4&#125;&#39;  </span><br><span class="line">(2) 缓冲区的free值：# free -m | grep - | awk &#39;&#123;print $4&#125;&#39;  </span><br><span class="line">(3) Swap分区free值：# free -m | grep Swap | awk &#39;&#123;print $4&#125;&#39;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>3、编写内存监控的脚本文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;scripts&#x2F;free-mem.sh  </span><br><span class="line">  </span><br><span class="line">#!&#x2F;bin&#x2F;bash   </span><br><span class="line">#使用free命令监控linux系统内存变化  </span><br><span class="line">  </span><br><span class="line">#取系统当前时间（以追加的方式写入文件&gt;&gt;）   </span><br><span class="line">date &gt;&gt; &#x2F;scripts&#x2F;date-time.txt  </span><br><span class="line">  </span><br><span class="line">#抓取物理内存free值（以追加的方式写入文件&gt;&gt;）   </span><br><span class="line">echo Mem-free: &#96;free -m | grep Mem | awk &#39;&#123;print $4&#125;&#39;&#96;M &gt;&gt; &#x2F;scripts&#x2F;mem-free.txt  </span><br><span class="line">  </span><br><span class="line">#抓取缓冲区的free值（以追加的方式写入文件&gt;&gt;）   </span><br><span class="line">echo buffers&#x2F;cache-free: &#96;free -m | grep - | awk &#39;&#123;print $4&#125;&#39;&#96;M &gt;&gt; &#x2F;scripts&#x2F;buffers-free.txt  </span><br><span class="line">  </span><br><span class="line">#抓取Swap分区free值（以追加的方式写入文件&gt;&gt;）   </span><br><span class="line">echo Swap-free: &#96;free -m | grep Swap | awk &#39;&#123;print $4&#125;&#39;&#96;M &gt;&gt; &#x2F;scripts&#x2F;swap-free.txt  </span><br><span class="line">  </span><br><span class="line">#逐行连接上面的时间和内存相关行数据（每次重新写入文件&gt;）   </span><br><span class="line">paste &#x2F;scripts&#x2F;date-time.txt &#x2F;scripts&#x2F;mem-free.txt &#x2F;scripts&#x2F;buffers-free.txt &#x2F;scripts&#x2F;swap-free.txt &gt; &#x2F;scripts&#x2F;freemem.txt  </span><br><span class="line">  </span><br><span class="line"># chmod a+x &#x2F;scripts&#x2F;free-mem.sh  </span><br><span class="line"># &#x2F;scripts&#x2F;free-mem.sh  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>4、查看内存监控的结果文件：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat &#x2F;scripts&#x2F;freemem.txt  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>5、编写free结果文件邮件发送脚本：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;scripts&#x2F;sendmail-mem.sh  </span><br><span class="line">  </span><br><span class="line">#!&#x2F;bin&#x2F;bash   </span><br><span class="line">#把生成的freemem.txt文件通过邮件发送给用户  </span><br><span class="line">  </span><br><span class="line">#提取本服务器的IP地址信息   </span><br><span class="line">IP&#x3D;&#96;ifconfig eth0 | grep &quot;inet addr&quot; | cut -f 2 -d &quot;:&quot; | cut -f 1 -d &quot; &quot;&#96;  </span><br><span class="line">  </span><br><span class="line">#提取当前日期时间   </span><br><span class="line">today&#x3D;&#96;date -d &quot;0 day&quot; +%Y年%m月%d日&#96;  </span><br><span class="line">  </span><br><span class="line">#发送内存监控结果邮件   </span><br><span class="line">echo &quot;这是$IP服务器$today的内存监控报告，请下载附件。&quot; | mutt -s &quot;$IP服务器$today内存监控报告&quot; -a &#x2F;scripts&#x2F;freemem.txt test@126.com  </span><br><span class="line">  </span><br><span class="line"># chmod a+x &#x2F;scripts&#x2F;sendmail-mem.sh  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>三、监控系统交换分区Swap的情况，当使用超过80%的时候发告警邮件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"># vim &#x2F;scripts&#x2F;swap-warning.sh  </span><br><span class="line">  </span><br><span class="line">#!&#x2F;bin&#x2F;bash  </span><br><span class="line">  </span><br><span class="line">#提取本服务器的IP地址信息   </span><br><span class="line">IP&#x3D;&#96;ifconfig eth0 | grep &quot;inet addr&quot; | cut -f 2 -d &quot;:&quot; | cut -f 1 -d &quot; &quot;&#96;  </span><br><span class="line">  </span><br><span class="line">#系统分配的交换分区总量   </span><br><span class="line">swap_total&#x3D;&#96;free -m | grep Swap | awk &#39;&#123;print $2&#125;&#39;&#96;  </span><br><span class="line">  </span><br><span class="line">#当前剩余的交换分区free大小   </span><br><span class="line">swap_free&#x3D;&#96;free -m | grep Swap | awk &#39;&#123;print $4&#125;&#39;&#96;  </span><br><span class="line">  </span><br><span class="line">#当前已使用的交换分区used大小   </span><br><span class="line">swap_used&#x3D;&#96;free -m | grep Swap | awk &#39;&#123;print $3&#125;&#39;&#96;  </span><br><span class="line">  </span><br><span class="line">if ((swap_used !&#x3D; 0)); then   </span><br><span class="line">  </span><br><span class="line">#如果交换分区已被使用，则计算当前剩余交换分区free所占总量的百分比，用小数来表示，要在小数点前面补一个整数位0   </span><br><span class="line">swap_per&#x3D;0&#96;echo &quot;scale&#x3D;2;$swap_free&#x2F;$swap_total&quot; | bc&#96;  </span><br><span class="line">  </span><br><span class="line">#设置交换分区的告警值为20%(即使用超过80%的时候告警)。  </span><br><span class="line">swap_warn&#x3D;0.20  </span><br><span class="line">  </span><br><span class="line">#当前剩余交换分区百分比与告警值进行比较（当大于告警值(即剩余20%以上)时会返回1，小于(即剩余不足20%)时会返回0 ）   </span><br><span class="line">swap_now&#x3D;&#96;expr $swap_per &gt; $swap_warn&#96;  </span><br><span class="line">  </span><br><span class="line">#如果当前交换分区使用超过80%（即剩余小于20%，上面的返回值等于0），立即发邮件告警   </span><br><span class="line">if (($swap_now &#x3D;&#x3D; 0)); then   </span><br><span class="line">echo &quot;$IP服务器swap交换分区只剩下 $swap_free M 未使用，剩余不足20%，使用率已经超过80%，请及时处理。&quot; | mutt -s &quot;$IP 服务器内存告警&quot; test@126.com  </span><br><span class="line">fi   </span><br><span class="line">  </span><br><span class="line">fi   </span><br><span class="line">  </span><br><span class="line"># chmod a+x &#x2F;scripts&#x2F;swap-warning.sh  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>四、加入任务计划：内存每十分钟检测一次，有告警则立即发邮件(十分钟发一次)，内存检测结果邮件每天早上8点发一次</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># crontab -e  </span><br><span class="line">  </span><br><span class="line">*&#x2F;10 * * * * &#x2F;scripts&#x2F;free-mem.sh   </span><br><span class="line">*&#x2F;10 * * * * &#x2F;scripts&#x2F;swap-warning.sh   </span><br><span class="line">0 8 * * * &#x2F;scripts&#x2F;sendmail-mem.sh  </span><br><span class="line">  </span><br><span class="line"># service crond restart  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><em>转自：ITPUB技术小栈</em>  </p>
<p><img src=""></p>
<p><strong>**</strong>|<strong>**</strong> <strong>**</strong>公众号专属福利 1**<strong>** ****</strong>|<strong>**</strong> </p>
<p><strong>**</strong>| 2020全新专题实战教程限时1.99元 |<strong>**</strong> </p>
<p><strong><strong>****</strong></strong>|<strong>**</strong>识**<strong><strong>**</strong></strong>别获取**<strong><strong>**</strong></strong>课程**<strong><strong>**</strong></strong>邀请好友听课即可返现9**<strong><strong><strong><strong><strong><strong><strong><strong>**</strong></strong></strong></strong></strong></strong></strong></strong>9%|<strong><strong><strong><strong>**</strong></strong></strong></strong></p>
<p><strong><strong>****</strong></strong>|<strong><strong><strong><strong>**</strong></strong></strong></strong>如不分享也可识别海报中二维码单独购买 |<strong>**</strong> </p>
<p><img src=""></p>
<p><img src=""></p>
<p><img src=""></p>
<p><img src=""></p>
<p><img src=""></p>
<p><img src=""></p>
<p>以docker为首的容器技术被称之为云计算时代的未来，谁掌握了微服务容器技术，谁就掌握了未来的核心技术，锁定10月31日晚20:00，理论结合实战带你学好docker！</p>
<p><img src=""></p>
<p>作者：hrs219 </p>
<p>链接：<a target="_blank" rel="noopener" href="http://huangrs.blog.51cto.com/2677571/788379">http://huangrs.blog.51cto.com/2677571/788379</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-linux/怎么理解linux的平均负载及平均负载高后的排查工具"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/linux/%E6%80%8E%E4%B9%88%E7%90%86%E8%A7%A3linux%E7%9A%84%E5%B9%B3%E5%9D%87%E8%B4%9F%E8%BD%BD%E5%8F%8A%E5%B9%B3%E5%9D%87%E8%B4%9F%E8%BD%BD%E9%AB%98%E5%90%8E%E7%9A%84%E6%8E%92%E6%9F%A5%E5%B7%A5%E5%85%B7/"
    >怎么理解linux的平均负载及平均负载高后的排查工具.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/linux/%E6%80%8E%E4%B9%88%E7%90%86%E8%A7%A3linux%E7%9A%84%E5%B9%B3%E5%9D%87%E8%B4%9F%E8%BD%BD%E5%8F%8A%E5%B9%B3%E5%9D%87%E8%B4%9F%E8%BD%BD%E9%AB%98%E5%90%8E%E7%9A%84%E6%8E%92%E6%9F%A5%E5%B7%A5%E5%85%B7/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/linux/">linux</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="怎么理解linux的平均负载及平均负载高后的排查工具"><a href="#怎么理解linux的平均负载及平均负载高后的排查工具" class="headerlink" title="怎么理解linux的平均负载及平均负载高后的排查工具"></a>怎么理解linux的平均负载及平均负载高后的排查工具</h1><h2 id="什么是平均负载"><a href="#什么是平均负载" class="headerlink" title="什么是平均负载"></a>什么是平均负载</h2><p>平均负载可以对于我们来说及熟悉又陌生，但我们问平均负载是什么，但大部分人都回答说平均负载不就是单位时间内CPU使用率吗？其实并不是这样的，如果可以的话，可以 man uptime 来了解一下平均负载的详细信息。</p>
<p>简单的说平均负载是指单位时间内，系统处于<strong>可运行状态和不可中断状态</strong>的平均进程数，也就是说<strong>平均活跃进程数</strong>，它和CPU使用率并没有直接关系。这里解释一下可运行状态和不可中断这两个词。</p>
<p>可运行状态：</p>
<ul>
<li>指正在使用CPU或者正在等待CPU的进程，我们使用ps命令查看处于R状态的进程</li>
</ul>
<p>不可中断状态：</p>
<ul>
<li>进程则是正处于内核态关键流程中的进程，并且这些流程是不可中断的。例如：常见的等待硬件设备I/O的响应，也就是我们在ps命令查看处于D状态的进程</li>
</ul>
<p>比如，当一个进程向磁盘读写数据时，为了保证数据的一致性，在得到磁盘回复前，它是不能被其他进程中断或者打断的，这个时候的进程处于不可中断状态，如果此时的进程被打断了，就容易出现磁盘数据和进程数据不一致的问题。</p>
<p>所以，<strong>不可中断状态实际上是系统进程和硬件设备的一种保护机制。</strong></p>
<p>因此，你可以简单理解为，平均负载就是平均活跃进程数。平均活跃进程数，直观上的理解就是单位时间内的活跃进程数，但它实际上是活跃进程数的指数衰减平均值。既然是平均活跃进程数，那么理想状态，就是每个CPU上都刚好运行着一个进程，这样每个CPU都会得到充分的利用。例如平均负载为2时，意味着什么呢？</p>
<ul>
<li>在只有2个CPU的系统上，意味着所有的CPU刚好被完全占用</li>
<li>在4个CPU的系统上，意味着CPU有50%的空闲</li>
<li>而在只有1个CPU的系统上，则意味着有一半的进程竞争不到CPU</li>
</ul>
<h2 id="平均负载和CPU使用率"><a href="#平均负载和CPU使用率" class="headerlink" title="平均负载和CPU使用率"></a>平均负载和CPU使用率</h2><p>现实工作中，我们经常容易把平均负载和CPU使用率混淆，所以在这里，我也做一个分区。</p>
<p>可能你会疑惑，既然平均负载代表的是活跃进程数，那平均负载高了，不就意味着CPU使用率高吗？</p>
<p>我们还是要回到平均负载的含义上来，平均负载是指单位时间内，处于可运行状态和不可中断状态的进程数，所以，它不仅包括了正常使用CPU的进程，还包括了等待CPU和等待I/O的进程。</p>
<p>而CPU使用率，是单位时间内CPU的繁忙情况的统计，跟平均负载并不一定完全对应，例如：</p>
<ul>
<li>CPU密集型进程，使用大量CPU会导致平均负载升高，此时这两者是一致的</li>
<li>I/O密集型进程，等待I/O也会导致平均负载升高，但CPU使用率不一定很高</li>
<li>大量等待CPU的进程调度也会导致平均负载升高，此时的CPU使用率会很高</li>
</ul>
<h2 id="平均负载案例"><a href="#平均负载案例" class="headerlink" title="平均负载案例"></a>平均负载案例</h2><p>这里我们需要安装几个工具sysstat、stress、stress-ng</p>
<p>这里Centos的sysstat版本会老一点，最好升级到最新版本。手动rpm安装或者源码安装</p>
<h3 id="场景一、CPU密集型"><a href="#场景一、CPU密集型" class="headerlink" title="场景一、CPU密集型"></a>场景一、CPU密集型</h3><p>1、运行一个stress命令，模拟一个CPU使用率100%场景</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ stress --cpu 1 --timeout 600</span><br></pre></td></tr></table></figure>

<p>2、开启第二个终端，uptime查看平均负载的变化情况</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ watch -d uptime</span><br><span class="line"> 09:40:35 up 80 days, 18:41,  2 users,  load average: 1.62, 1.10, 0.87</span><br></pre></td></tr></table></figure>

<p>3、开启第三个终端，mpstat 查看CPU使用率的变化情况</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ mpstat -P ALL 5 2010:06:37 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle10:06:42 AM  all   31.50    0.00    0.35    0.00    0.00    0.00    0.00    0.00    0.00   68.1510:06:42 AM    0    1.20    0.00    0.80    0.00    0.00    0.00    0.00    0.00    0.00   98.0010:06:42 AM    1    7.21    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00   92.3810:06:42 AM    2  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.0010:06:42 AM    3   17.43    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00   82.36</span><br><span class="line"># -P ALL 表示监控所有CPU，后面数字5 表示间隔5秒输出一次数据</span><br></pre></td></tr></table></figure>

<p>从第二个终端可以看到，1分钟平均负载增加到1.62，从第三个终端我们可以看到有一个CPU使用率100%，但iowait为0，这说明平均负载的升高正式由CPU使用率为100%</p>
<p>那我们查看是那个进程导致了CPU使用率为100%呢？我们可以使用pidstat来查看：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">#每5秒输出一次数据$ pidstat -u 5 1 </span><br><span class="line">10:08:41 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command</span><br><span class="line">10:08:46 AM     0         1    0.20    0.00    0.00    0.00    0.20     0  systemd</span><br><span class="line">10:08:46 AM     0       599    0.00    1.00    0.00    0.20    1.00     0  systemd-journal</span><br><span class="line">10:08:46 AM     0      1043    0.60    0.00    0.00    0.00    0.60     0  rsyslogd</span><br><span class="line">10:08:46 AM     0      6863  100.00    0.00    0.00    0.00  100.00     3  stress</span><br><span class="line">10:08:46 AM     0      7303    0.20    0.20    0.00    0.00    0.40     2  pidstat</span><br></pre></td></tr></table></figure>

<p>从这里我们可以看到是stress这个进程导致的。</p>
<h3 id="场景二、I-O密集型进程"><a href="#场景二、I-O密集型进程" class="headerlink" title="场景二、I/O密集型进程"></a>场景二、I/O密集型进程</h3><p>1、我们使用stress-ng命令，但这次模拟I/O压力，既不停执行sync:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#--hdd表示读写临时文件#-i 生成几个worker循环调用sync()产生io压力</span><br><span class="line">$ stress-ng -i 4 --hdd 1 --timeout 600</span><br></pre></td></tr></table></figure>

<p>2、开启第二个终端运行uptime查看平均负载情况</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ watch -d uptime </span><br><span class="line"> 10:30:57 up 98 days, 19:39,  3 users,  load average: 1.71, 0.75, 0.69</span><br></pre></td></tr></table></figure>

<p>3、开启第三个终端运行mpstat查看CPU使用率</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ mpstat -P ALL 5 20</span><br><span class="line">10:32:09 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle</span><br><span class="line">10:32:14 AM  all    6.80    0.00   33.75   26.16    0.00    0.39    0.00    0.00    0.00   32.90</span><br><span class="line">10:32:14 AM    0    4.03    0.00   69.57   19.91    0.00    0.00    0.00    0.00    0.00    6.49</span><br><span class="line">10:32:14 AM    1   25.32    0.00    9.49    0.00    0.00    0.95    0.00    0.00    0.00   64.24</span><br><span class="line">10:32:14 AM    2    0.24    0.00   10.87   63.04    0.00    0.48    0.00    0.00    0.00   25.36</span><br><span class="line">10:32:14 AM    3    1.42    0.00   36.93   14.20    0.00    0.28    0.00    0.00    0.00   47.16</span><br></pre></td></tr></table></figure>

<p>从这里可以看到，1分钟平均负载会慢慢增加到1.71，其中一个CPU的系统CPU使用率升到63.04。这说明，平均负载的升高是由于iowait升高。</p>
<p>那么我们到底是哪个进程导致的呢？我们使用pidstat来查看：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ pidstat -u 5 1</span><br><span class="line">Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command</span><br><span class="line">Average:        0         1    0.00    0.19    0.00    0.00    0.19     -  systemd</span><br><span class="line">Average:        0        10    0.00    0.19    0.00    1.56    0.19     -  rcu_sched</span><br><span class="line">Average:        0       599    0.58    1.75    0.00    0.39    2.33     -  systemd-journal</span><br><span class="line">Average:        0      1043    0.19    0.19    0.00    0.00    0.39     -  rsyslogd</span><br><span class="line">Average:        0      6934    0.00    1.56    0.00    1.17    1.56     -  kworker&#x2F;2:0-events_power_efficient</span><br><span class="line">Average:        0      7383    0.00    0.39    0.00    0.78    0.39     -  kworker&#x2F;1:0-events_power_efficient</span><br><span class="line">Average:        0      9411    0.00    0.19    0.00    0.58    0.19     -  kworker&#x2F;0:0-events</span><br><span class="line">Average:        0      9662    0.00   97.67    0.00    0.19   97.67     -  kworker&#x2F;u8:0+flush-253:0</span><br><span class="line">Average:        0     10793    0.00    0.97    0.00    1.56    0.97     -  kworker&#x2F;3:2-mm_percpu_wq</span><br><span class="line">Average:        0     11062    0.00   21.79    0.00    0.19   21.79     -  stress-ng-hdd</span><br><span class="line">Average:        0     11063    0.00    1.95    0.00    1.36    1.95     -  stress-ng-io</span><br><span class="line">Average:        0     11064    0.00    2.72    0.00    0.39    2.72     -  stress-ng-io</span><br><span class="line">Average:        0     11065    0.00    1.36    0.00    1.75    1.36     -  stress-ng-io</span><br><span class="line">Average:        0     11066    0.00    2.72    0.00    0.58    2.72     -  stress-ng-io</span><br></pre></td></tr></table></figure>

<p>可以发现是stress-ng导致的</p>
<h3 id="场景三、大量进程的场景"><a href="#场景三、大量进程的场景" class="headerlink" title="场景三、大量进程的场景"></a>场景三、大量进程的场景</h3><p>当系统中运行进程超出CPU运行能力时，就会出现等待CPU的进程。</p>
<p>比如：我们使用stress,但这次模拟8个进程：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ stress -c 8 --timeout 600</span><br></pre></td></tr></table></figure>

<p>我们的系统只有4颗CPU，这时候要运行8个进程，是明显不够的，系统的CPU后严重过载,这时候负载值达到了4点多：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$  uptime</span><br><span class="line"> 10:56:22 up 98 days, 20:05,  3 users,  load average: 4.52, 2.82, 2.67</span><br></pre></td></tr></table></figure>

<p>接着我们运行pidstat来查看一下进程的情况：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ pidstat -u 5 1</span><br><span class="line">Linux 5.0.5-1.el7.elrepo.x86_64 (k8s-m1)     07&#x2F;11&#x2F;2019     _x86_64_    (4 CPU)</span><br><span class="line"></span><br><span class="line">10:57:33 AM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command</span><br><span class="line">10:57:38 AM     0         1    0.20    0.00    0.00    0.00    0.20     1  systemd</span><br><span class="line">10:57:38 AM     0       599    0.00    0.99    0.00    0.20    0.99     2  systemd-journal</span><br><span class="line">10:57:38 AM     0      1043    0.60    0.20    0.00    0.00    0.79     1  rsyslogd</span><br><span class="line">10:57:38 AM     0     12927   51.59    0.00    0.00   48.21   51.59     0  stress</span><br><span class="line">10:57:38 AM     0     12928   44.64    0.00    0.00   54.96   44.64     0  stress</span><br><span class="line">10:57:38 AM     0     12929   45.44    0.00    0.00   54.56   45.44     2  stress</span><br><span class="line">10:57:38 AM     0     12930   45.44    0.00    0.00   54.37   45.44     2  stress</span><br><span class="line">10:57:38 AM     0     12931   51.59    0.00    0.00   48.21   51.59     3  stress</span><br><span class="line">10:57:38 AM     0     12932   48.41    0.00    0.00   51.19   48.41     1  stress</span><br><span class="line">10:57:38 AM     0     12933   45.24    0.00    0.00   54.37   45.24     3  stress</span><br><span class="line">10:57:38 AM     0     12934   48.81    0.00    0.00   50.99   48.81     1  stress</span><br><span class="line">10:57:38 AM     0     13083    0.00    0.40    0.00    0.20    0.40     0  pidstat</span><br></pre></td></tr></table></figure>

<p>可以看出，8个进程抢占4颗CPU，每个进程等到CPU时间(%wait)高达50%，这些都超出CPU计算能力的进程，最终导致CPU过载。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
  </article>
  

  
  <nav class="page-nav">
    
    <a class="extend prev" rel="prev" href="/page/5/">上一页</a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><span class="page-number current">6</span><a class="page-number" href="/page/7/">7</a><a class="page-number" href="/page/8/">8</a><span class="space">&hellip;</span><a class="page-number" href="/page/14/">14</a><a class="extend next" rel="next" href="/page/7/">下一页</a>
  </nav>
  
</section>
</div>

      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2020
        <i class="ri-heart-fill heart_icon"></i> TzWind
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="Hexo"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://www.baidu.com">百度</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>

</html>